Merge branch 'dev' into optolink

This commit is contained in:
j0ta29 2023-07-24 17:12:27 +02:00 committed by GitHub
commit 28236c9db4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 410 additions and 128 deletions

2
.gitignore vendored
View file

@ -130,3 +130,5 @@ sdkconfig.*
!sdkconfig.defaults !sdkconfig.defaults
.tests/ .tests/
/components

View file

@ -22,16 +22,22 @@ RUN \
python3=3.9.2-3 \ python3=3.9.2-3 \
python3-pip=20.3.4-4+deb11u1 \ python3-pip=20.3.4-4+deb11u1 \
python3-setuptools=52.0.0-4 \ python3-setuptools=52.0.0-4 \
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
python3-cryptography=3.3.2-1 \ python3-cryptography=3.3.2-1 \
python3-venv=3.9.2-3 \ python3-venv=3.9.2-3 \
iputils-ping=3:20210202-1 \ iputils-ping=3:20210202-1 \
git=1:2.30.2-1+deb11u2 \ git=1:2.30.2-1+deb11u2 \
curl=7.74.0-1.3+deb11u7 \ curl=7.74.0-1.3+deb11u7 \
openssh-client=1:8.4p1-5+deb11u1 \ openssh-client=1:8.4p1-5+deb11u1 \
libcairo2=1.16.0-5 \ python3-cffi=1.14.5-1; \
python3-cffi=1.14.5-1 \ if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
&& rm -rf \ apt-get install -y --no-install-recommends \
build-essential=12.9 \
python3-dev=3.9.2-3 \
zlib1g-dev=1:1.2.11.dfsg-2+deb11u2 \
libjpeg-dev=1:2.0.6-4 \
libcairo2=1.16.0-5; \
fi; \
rm -rf \
/tmp/* \ /tmp/* \
/var/{cache,log}/* \ /var/{cache,log}/* \
/var/lib/apt/lists/* /var/lib/apt/lists/*

View file

@ -365,10 +365,16 @@ def command_wizard(args):
def command_config(args, config): def command_config(args, config):
_LOGGER.info("Configuration is valid!")
if not CORE.verbose: if not CORE.verbose:
config = strip_default_ids(config) config = strip_default_ids(config)
safe_print(yaml_util.dump(config, args.show_secrets)) output = yaml_util.dump(config, args.show_secrets)
# add the console decoration so the front-end can hide the secrets
if not args.show_secrets:
output = re.sub(
r"(password|key|psk|ssid)\:\s(.*)", r"\1: \\033[5m\2\\033[6m", output
)
safe_print(output)
_LOGGER.info("Configuration is valid!")
return 0 return 0

View file

@ -1420,6 +1420,7 @@ message VoiceAssistantRequest {
bool start = 1; bool start = 1;
string conversation_id = 2; string conversation_id = 2;
bool use_vad = 3;
} }
message VoiceAssistantResponse { message VoiceAssistantResponse {

View file

@ -907,12 +907,13 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id) { bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad) {
if (!this->voice_assistant_subscription_) if (!this->voice_assistant_subscription_)
return false; return false;
VoiceAssistantRequest msg; VoiceAssistantRequest msg;
msg.start = start; msg.start = start;
msg.conversation_id = conversation_id; msg.conversation_id = conversation_id;
msg.use_vad = use_vad;
return this->send_voice_assistant_request(msg); return this->send_voice_assistant_request(msg);
} }
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) { void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {

View file

@ -124,7 +124,7 @@ class APIConnection : public APIServerConnection {
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override { void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
this->voice_assistant_subscription_ = msg.subscribe; this->voice_assistant_subscription_ = msg.subscribe;
} }
bool request_voice_assistant(bool start, const std::string &conversation_id); bool request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad);
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override; void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override; void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
#endif #endif

View file

@ -6348,6 +6348,10 @@ bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
this->start = value.as_bool(); this->start = value.as_bool();
return true; return true;
} }
case 3: {
this->use_vad = value.as_bool();
return true;
}
default: default:
return false; return false;
} }
@ -6365,6 +6369,7 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->start); buffer.encode_bool(1, this->start);
buffer.encode_string(2, this->conversation_id); buffer.encode_string(2, this->conversation_id);
buffer.encode_bool(3, this->use_vad);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantRequest::dump_to(std::string &out) const { void VoiceAssistantRequest::dump_to(std::string &out) const {
@ -6377,6 +6382,10 @@ void VoiceAssistantRequest::dump_to(std::string &out) const {
out.append(" conversation_id: "); out.append(" conversation_id: ");
out.append("'").append(this->conversation_id).append("'"); out.append("'").append(this->conversation_id).append("'");
out.append("\n"); out.append("\n");
out.append(" use_vad: ");
out.append(YESNO(this->use_vad));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif

View file

@ -1655,6 +1655,7 @@ class VoiceAssistantRequest : public ProtoMessage {
public: public:
bool start{false}; bool start{false};
std::string conversation_id{}; std::string conversation_id{};
bool use_vad{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;

View file

@ -323,16 +323,16 @@ void APIServer::on_shutdown() {
} }
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool APIServer::start_voice_assistant(const std::string &conversation_id) { bool APIServer::start_voice_assistant(const std::string &conversation_id, bool use_vad) {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (c->request_voice_assistant(true, conversation_id)) if (c->request_voice_assistant(true, conversation_id, use_vad))
return true; return true;
} }
return false; return false;
} }
void APIServer::stop_voice_assistant() { void APIServer::stop_voice_assistant() {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (c->request_voice_assistant(false, "")) if (c->request_voice_assistant(false, "", false))
return; return;
} }
} }

View file

@ -81,7 +81,7 @@ class APIServer : public Component, public Controller {
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool start_voice_assistant(const std::string &conversation_id); bool start_voice_assistant(const std::string &conversation_id, bool use_vad);
void stop_voice_assistant(); void stop_voice_assistant();
#endif #endif

View file

@ -114,7 +114,7 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei
if (!decoded.has_value()) if (!decoded.has_value())
return false; return false;
// Decoded remote state y 3 bytes long code. // Decoded remote state y 3 bytes long code.
uint32_t remote_state = *decoded; uint32_t remote_state = (*decoded).second;
ESP_LOGV(TAG, "Decoded 0x%06X", remote_state); ESP_LOGV(TAG, "Decoded 0x%06X", remote_state);
if ((remote_state & 0xFF0000) != 0xB20000) if ((remote_state & 0xFF0000) != 0xB20000)
return false; return false;

View file

@ -51,7 +51,7 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet
if (universe < first_universe_ || universe > get_last_universe()) if (universe < first_universe_ || universe > get_last_universe())
return false; return false;
int output_offset = (universe - first_universe_) * get_lights_per_universe(); int32_t output_offset = (universe - first_universe_) * get_lights_per_universe();
// limit amount of lights per universe and received // limit amount of lights per universe and received
int output_end = int output_end =
std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1)); std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1));

View file

@ -1,5 +1,5 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Union from typing import Union, Optional
from pathlib import Path from pathlib import Path
import logging import logging
import os import os
@ -42,6 +42,7 @@ from .const import ( # noqa
KEY_REFRESH, KEY_REFRESH,
KEY_REPO, KEY_REPO,
KEY_SDKCONFIG_OPTIONS, KEY_SDKCONFIG_OPTIONS,
KEY_SUBMODULES,
KEY_VARIANT, KEY_VARIANT,
VARIANT_ESP32C3, VARIANT_ESP32C3,
VARIANT_FRIENDLY, VARIANT_FRIENDLY,
@ -120,17 +121,28 @@ def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType):
def add_idf_component( def add_idf_component(
name: str, repo: str, ref: str = None, path: str = None, refresh: TimePeriod = None *,
name: str,
repo: str,
ref: str = None,
path: str = None,
refresh: TimePeriod = None,
components: Optional[list[str]] = None,
submodules: Optional[list[str]] = None,
): ):
"""Add an esp-idf component to the project.""" """Add an esp-idf component to the project."""
if not CORE.using_esp_idf: if not CORE.using_esp_idf:
raise ValueError("Not an esp-idf project") raise ValueError("Not an esp-idf project")
if components is None:
components = []
if name not in CORE.data[KEY_ESP32][KEY_COMPONENTS]: if name not in CORE.data[KEY_ESP32][KEY_COMPONENTS]:
CORE.data[KEY_ESP32][KEY_COMPONENTS][name] = { CORE.data[KEY_ESP32][KEY_COMPONENTS][name] = {
KEY_REPO: repo, KEY_REPO: repo,
KEY_REF: ref, KEY_REF: ref,
KEY_PATH: path, KEY_PATH: path,
KEY_REFRESH: refresh, KEY_REFRESH: refresh,
KEY_COMPONENTS: components,
KEY_SUBMODULES: submodules,
} }
@ -163,23 +175,23 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5)
# The platformio/espressif32 version to use for arduino frameworks # The platformio/espressif32 version to use for arduino frameworks
# - https://github.com/platformio/platform-espressif32/releases # - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
ARDUINO_PLATFORM_VERSION = cv.Version(5, 3, 0) ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
# The default/recommended esp-idf framework version # The default/recommended esp-idf framework version
# - https://github.com/espressif/esp-idf/releases # - https://github.com/espressif/esp-idf/releases
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf # - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 4) RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 5)
# The platformio/espressif32 version to use for esp-idf frameworks # The platformio/espressif32 version to use for esp-idf frameworks
# - https://github.com/platformio/platform-espressif32/releases # - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32 # - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
ESP_IDF_PLATFORM_VERSION = cv.Version(5, 3, 0) ESP_IDF_PLATFORM_VERSION = cv.Version(5, 4, 0)
def _arduino_check_versions(value): def _arduino_check_versions(value):
value = value.copy() value = value.copy()
lookups = { lookups = {
"dev": (cv.Version(2, 1, 0), "https://github.com/espressif/arduino-esp32.git"), "dev": (cv.Version(2, 1, 0), "https://github.com/espressif/arduino-esp32.git"),
"latest": (cv.Version(2, 0, 7), None), "latest": (cv.Version(2, 0, 9), None),
"recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None),
} }
@ -214,7 +226,7 @@ def _esp_idf_check_versions(value):
value = value.copy() value = value.copy()
lookups = { lookups = {
"dev": (cv.Version(5, 1, 0), "https://github.com/espressif/esp-idf.git"), "dev": (cv.Version(5, 1, 0), "https://github.com/espressif/esp-idf.git"),
"latest": (cv.Version(5, 0, 1), None), "latest": (cv.Version(5, 1, 0), None),
"recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None),
} }
@ -536,20 +548,41 @@ def copy_files():
ref=component[KEY_REF], ref=component[KEY_REF],
refresh=component[KEY_REFRESH], refresh=component[KEY_REFRESH],
domain="idf_components", domain="idf_components",
submodules=component[KEY_SUBMODULES],
) )
mkdir_p(CORE.relative_build_path("components")) mkdir_p(CORE.relative_build_path("components"))
component_dir = repo_dir component_dir = repo_dir
if component[KEY_PATH] is not None: if component[KEY_PATH] is not None:
component_dir = component_dir / component[KEY_PATH] component_dir = component_dir / component[KEY_PATH]
shutil.copytree( if component[KEY_COMPONENTS] == ["*"]:
component_dir, shutil.copytree(
CORE.relative_build_path(f"components/{name}"), component_dir,
dirs_exist_ok=True, CORE.relative_build_path("components"),
ignore=shutil.ignore_patterns(".git", ".github"), dirs_exist_ok=True,
symlinks=True, ignore=shutil.ignore_patterns(".git*"),
ignore_dangling_symlinks=True, symlinks=True,
) ignore_dangling_symlinks=True,
)
elif len(component[KEY_COMPONENTS]) > 0:
for comp in component[KEY_COMPONENTS]:
shutil.copytree(
component_dir / comp,
CORE.relative_build_path(f"components/{comp}"),
dirs_exist_ok=True,
ignore=shutil.ignore_patterns(".git*"),
symlinks=True,
ignore_dangling_symlinks=True,
)
else:
shutil.copytree(
component_dir,
CORE.relative_build_path(f"components/{name}"),
dirs_exist_ok=True,
ignore=shutil.ignore_patterns(".git*"),
symlinks=True,
ignore_dangling_symlinks=True,
)
dir = os.path.dirname(__file__) dir = os.path.dirname(__file__)
post_build_file = os.path.join(dir, "post_build.py.script") post_build_file = os.path.join(dir, "post_build.py.script")

View file

@ -1201,6 +1201,10 @@ BOARDS = {
"name": "BPI-Bit", "name": "BPI-Bit",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"bpi_leaf_s3": {
"name": "BPI-Leaf-S3",
"variant": VARIANT_ESP32S3,
},
"briki_abc_esp32": { "briki_abc_esp32": {
"name": "Briki ABC (MBC-WB) - ESP32", "name": "Briki ABC (MBC-WB) - ESP32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1217,6 +1221,10 @@ BOARDS = {
"name": "Connaxio's Espoir", "name": "Connaxio's Espoir",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"cytron_maker_feather_aiot_s3": {
"name": "Cytron Maker Feather AIoT S3",
"variant": VARIANT_ESP32S3,
},
"d-duino-32": { "d-duino-32": {
"name": "D-duino-32", "name": "D-duino-32",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1225,6 +1233,10 @@ BOARDS = {
"name": "Deneyap Kart 1A", "name": "Deneyap Kart 1A",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"deneyapkart1Av2": {
"name": "Deneyap Kart 1A v2",
"variant": VARIANT_ESP32S3,
},
"deneyapkartg": { "deneyapkartg": {
"name": "Deneyap Kart G", "name": "Deneyap Kart G",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
@ -1237,6 +1249,10 @@ BOARDS = {
"name": "Deneyap Mini", "name": "Deneyap Mini",
"variant": VARIANT_ESP32S2, "variant": VARIANT_ESP32S2,
}, },
"deneyapminiv2": {
"name": "Deneyap Mini v2",
"variant": VARIANT_ESP32S2,
},
"denky32": { "denky32": {
"name": "Denky32 (WROOM32)", "name": "Denky32 (WROOM32)",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1265,6 +1281,10 @@ BOARDS = {
"name": "Espressif ESP32-C3-DevKitM-1", "name": "Espressif ESP32-C3-DevKitM-1",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,
}, },
"esp32-c3-m1i-kit": {
"name": "Ai-Thinker ESP-C3-M1-I-Kit",
"variant": VARIANT_ESP32C3,
},
"esp32cam": { "esp32cam": {
"name": "AI Thinker ESP32-CAM", "name": "AI Thinker ESP32-CAM",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1329,6 +1349,10 @@ BOARDS = {
"name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)", "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)",
"variant": VARIANT_ESP32S3, "variant": VARIANT_ESP32S3,
}, },
"esp32-s3-korvo-2": {
"name": "Espressif ESP32-S3-Korvo-2",
"variant": VARIANT_ESP32S3,
},
"esp32thing": { "esp32thing": {
"name": "SparkFun ESP32 Thing", "name": "SparkFun ESP32 Thing",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
@ -1637,6 +1661,10 @@ BOARDS = {
"name": "Noduino Quantum", "name": "Noduino Quantum",
"variant": VARIANT_ESP32, "variant": VARIANT_ESP32,
}, },
"redpill_esp32s3": {
"name": "Munich Labs RedPill ESP32-S3",
"variant": VARIANT_ESP32S3,
},
"seeed_xiao_esp32c3": { "seeed_xiao_esp32c3": {
"name": "Seeed Studio XIAO ESP32C3", "name": "Seeed Studio XIAO ESP32C3",
"variant": VARIANT_ESP32C3, "variant": VARIANT_ESP32C3,

View file

@ -9,6 +9,7 @@ KEY_REPO = "repo"
KEY_REF = "ref" KEY_REF = "ref"
KEY_REFRESH = "refresh" KEY_REFRESH = "refresh"
KEY_PATH = "path" KEY_PATH = "path"
KEY_SUBMODULES = "submodules"
VARIANT_ESP32 = "ESP32" VARIANT_ESP32 = "ESP32"
VARIANT_ESP32S2 = "ESP32S2" VARIANT_ESP32S2 = "ESP32S2"

View file

@ -112,7 +112,6 @@ CONFIG_SCHEMA = cv.All(
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
"ld2410", "ld2410",
baud_rate=256000,
require_tx=True, require_tx=True,
require_rx=True, require_rx=True,
parity="NONE", parity="NONE",

View file

@ -86,10 +86,10 @@ async def to_code(config):
5, 0, 0 5, 0, 0
): ):
add_idf_component( add_idf_component(
"mdns", name="mdns",
"https://github.com/espressif/esp-protocols.git", repo="https://github.com/espressif/esp-protocols.git",
"mdns-v1.0.9", ref="mdns-v1.0.9",
"components/mdns", path="components/mdns",
) )
if config[CONF_DISABLED]: if config[CONF_DISABLED]:

View file

@ -28,6 +28,7 @@ from esphome.const import (
DEVICE_CLASS_DATA_RATE, DEVICE_CLASS_DATA_RATE,
DEVICE_CLASS_DATA_SIZE, DEVICE_CLASS_DATA_SIZE,
DEVICE_CLASS_DISTANCE, DEVICE_CLASS_DISTANCE,
DEVICE_CLASS_DURATION,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
DEVICE_CLASS_ENERGY_STORAGE, DEVICE_CLASS_ENERGY_STORAGE,
@ -42,6 +43,7 @@ from esphome.const import (
DEVICE_CLASS_NITROGEN_MONOXIDE, DEVICE_CLASS_NITROGEN_MONOXIDE,
DEVICE_CLASS_NITROUS_OXIDE, DEVICE_CLASS_NITROUS_OXIDE,
DEVICE_CLASS_OZONE, DEVICE_CLASS_OZONE,
DEVICE_CLASS_PH,
DEVICE_CLASS_PM1, DEVICE_CLASS_PM1,
DEVICE_CLASS_PM10, DEVICE_CLASS_PM10,
DEVICE_CLASS_PM25, DEVICE_CLASS_PM25,
@ -81,6 +83,7 @@ DEVICE_CLASSES = [
DEVICE_CLASS_DATA_RATE, DEVICE_CLASS_DATA_RATE,
DEVICE_CLASS_DATA_SIZE, DEVICE_CLASS_DATA_SIZE,
DEVICE_CLASS_DISTANCE, DEVICE_CLASS_DISTANCE,
DEVICE_CLASS_DURATION,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
DEVICE_CLASS_ENERGY_STORAGE, DEVICE_CLASS_ENERGY_STORAGE,
@ -95,6 +98,7 @@ DEVICE_CLASSES = [
DEVICE_CLASS_NITROGEN_MONOXIDE, DEVICE_CLASS_NITROGEN_MONOXIDE,
DEVICE_CLASS_NITROUS_OXIDE, DEVICE_CLASS_NITROUS_OXIDE,
DEVICE_CLASS_OZONE, DEVICE_CLASS_OZONE,
DEVICE_CLASS_PH,
DEVICE_CLASS_PM1, DEVICE_CLASS_PM1,
DEVICE_CLASS_PM10, DEVICE_CLASS_PM10,
DEVICE_CLASS_PM25, DEVICE_CLASS_PM25,

View file

@ -17,6 +17,7 @@ from esphome.const import (
CONF_PROTOCOL, CONF_PROTOCOL,
CONF_GROUP, CONF_GROUP,
CONF_DEVICE, CONF_DEVICE,
CONF_SECOND,
CONF_STATE, CONF_STATE,
CONF_CHANNEL, CONF_CHANNEL,
CONF_FAMILY, CONF_FAMILY,
@ -39,6 +40,7 @@ AUTO_LOAD = ["binary_sensor"]
CONF_RECEIVER_ID = "receiver_id" CONF_RECEIVER_ID = "receiver_id"
CONF_TRANSMITTER_ID = "transmitter_id" CONF_TRANSMITTER_ID = "transmitter_id"
CONF_FIRST = "first"
ns = remote_base_ns = cg.esphome_ns.namespace("remote_base") ns = remote_base_ns = cg.esphome_ns.namespace("remote_base")
RemoteProtocol = ns.class_("RemoteProtocol") RemoteProtocol = ns.class_("RemoteProtocol")
@ -349,19 +351,48 @@ async def canalsatld_action(var, config, args):
CoolixAction, CoolixAction,
CoolixDumper, CoolixDumper,
) = declare_protocol("Coolix") ) = declare_protocol("Coolix")
COOLIX_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t})
@register_binary_sensor("coolix", CoolixBinarySensor, COOLIX_SCHEMA) COOLIX_BASE_SCHEMA = cv.Schema(
{
cv.Required(CONF_FIRST): cv.hex_int_range(0, 16777215),
cv.Optional(CONF_SECOND, default=0): cv.hex_int_range(0, 16777215),
cv.Optional(CONF_DATA): cv.invalid(
"'data' option has been removed in ESPHome 2023.8. "
"Use the 'first' and 'second' options instead."
),
}
)
COOLIX_SENSOR_SCHEMA = cv.Any(cv.hex_int_range(0, 16777215), COOLIX_BASE_SCHEMA)
@register_binary_sensor("coolix", CoolixBinarySensor, COOLIX_SENSOR_SCHEMA)
def coolix_binary_sensor(var, config): def coolix_binary_sensor(var, config):
cg.add( if isinstance(config, dict):
var.set_data( cg.add(
cg.StructInitializer( var.set_data(
CoolixData, cg.StructInitializer(
("data", config[CONF_DATA]), CoolixData,
("first", config[CONF_FIRST]),
("second", config[CONF_SECOND]),
)
) )
) )
) else:
cg.add(
var.set_data(
cg.StructInitializer(CoolixData, ("first", 0), ("second", config))
)
)
@register_action("coolix", CoolixAction, COOLIX_BASE_SCHEMA)
async def coolix_action(var, config, args):
template_ = await cg.templatable(config[CONF_FIRST], args, cg.uint32)
cg.add(var.set_first(template_))
template_ = await cg.templatable(config[CONF_SECOND], args, cg.uint32)
cg.add(var.set_second(template_))
@register_trigger("coolix", CoolixTrigger, CoolixData) @register_trigger("coolix", CoolixTrigger, CoolixData)
@ -374,12 +405,6 @@ def coolix_dumper(var, config):
pass pass
@register_action("coolix", CoolixAction, COOLIX_SCHEMA)
async def coolix_action(var, config, args):
template_ = await cg.templatable(config[CONF_DATA], args, cg.uint32)
cg.add(var.set_data(template_))
# Dish # Dish
DishData, DishBinarySensor, DishTrigger, DishAction, DishDumper = declare_protocol( DishData, DishBinarySensor, DishTrigger, DishAction, DishDumper = declare_protocol(
"Dish" "Dish"

View file

@ -15,11 +15,21 @@ static const int32_t BIT_ZERO_SPACE_US = 1 * TICK_US;
static const int32_t FOOTER_MARK_US = 1 * TICK_US; static const int32_t FOOTER_MARK_US = 1 * TICK_US;
static const int32_t FOOTER_SPACE_US = 10 * TICK_US; static const int32_t FOOTER_SPACE_US = 10 * TICK_US;
static void encode_data(RemoteTransmitData *dst, const CoolixData &src) { bool CoolixData::operator==(const CoolixData &other) const {
// Break data into bytes, starting at the Most Significant if (this->first == 0)
// Byte. Each byte then being sent normal, then followed inverted. return this->second == other.first || this->second == other.second;
if (other.first == 0)
return other.second == this->first || other.second == this->second;
return this->first == other.first && this->second == other.second;
}
static void encode_frame(RemoteTransmitData *dst, const uint32_t &src) {
// Append header
dst->item(HEADER_MARK_US, HEADER_SPACE_US);
// Break data into bytes, starting at the Most Significant
// Byte. Each byte then being sent normal, then followed inverted.
for (unsigned shift = 16;; shift -= 8) { for (unsigned shift = 16;; shift -= 8) {
// Grab a bytes worth of data. // Grab a bytes worth of data
const uint8_t byte = src >> shift; const uint8_t byte = src >> shift;
// Normal // Normal
for (uint8_t mask = 1 << 7; mask; mask >>= 1) for (uint8_t mask = 1 << 7; mask; mask >>= 1)
@ -27,27 +37,33 @@ static void encode_data(RemoteTransmitData *dst, const CoolixData &src) {
// Inverted // Inverted
for (uint8_t mask = 1 << 7; mask; mask >>= 1) for (uint8_t mask = 1 << 7; mask; mask >>= 1)
dst->item(BIT_MARK_US, (byte & mask) ? BIT_ZERO_SPACE_US : BIT_ONE_SPACE_US); dst->item(BIT_MARK_US, (byte & mask) ? BIT_ZERO_SPACE_US : BIT_ONE_SPACE_US);
// Data end // End of frame
if (shift == 0) if (shift == 0) {
// Append footer
dst->mark(FOOTER_MARK_US);
break; break;
}
} }
} }
void CoolixProtocol::encode(RemoteTransmitData *dst, const CoolixData &data) { void CoolixProtocol::encode(RemoteTransmitData *dst, const CoolixData &data) {
dst->set_carrier_frequency(38000); dst->set_carrier_frequency(38000);
dst->reserve(2 + 2 * 48 + 2 + 2 + 2 * 48 + 1); dst->reserve(100 + 100 * data.has_second());
dst->item(HEADER_MARK_US, HEADER_SPACE_US); encode_frame(dst, data.first);
encode_data(dst, data); if (data.has_second()) {
dst->item(FOOTER_MARK_US, FOOTER_SPACE_US); dst->space(FOOTER_SPACE_US);
dst->item(HEADER_MARK_US, HEADER_SPACE_US); encode_frame(dst, data.second);
encode_data(dst, data); }
dst->mark(FOOTER_MARK_US);
} }
static bool decode_data(RemoteReceiveData &src, CoolixData &dst) { static bool decode_frame(RemoteReceiveData &src, uint32_t &dst) {
// Checking for header
if (!src.expect_item(HEADER_MARK_US, HEADER_SPACE_US))
return false;
// Reading data
uint32_t data = 0; uint32_t data = 0;
for (unsigned n = 3;; data <<= 8) { for (unsigned n = 3;; data <<= 8) {
// Read byte // Reading byte
for (uint32_t mask = 1 << 7; mask; mask >>= 1) { for (uint32_t mask = 1 << 7; mask; mask >>= 1) {
if (!src.expect_mark(BIT_MARK_US)) if (!src.expect_mark(BIT_MARK_US))
return false; return false;
@ -57,13 +73,16 @@ static bool decode_data(RemoteReceiveData &src, CoolixData &dst) {
return false; return false;
} }
} }
// Check for inverse byte // Checking for inverted byte
for (uint32_t mask = 1 << 7; mask; mask >>= 1) { for (uint32_t mask = 1 << 7; mask; mask >>= 1) {
if (!src.expect_item(BIT_MARK_US, (data & mask) ? BIT_ZERO_SPACE_US : BIT_ONE_SPACE_US)) if (!src.expect_item(BIT_MARK_US, (data & mask) ? BIT_ZERO_SPACE_US : BIT_ONE_SPACE_US))
return false; return false;
} }
// Checking the end of reading // End of frame
if (--n == 0) { if (--n == 0) {
// Checking for footer
if (!src.expect_mark(FOOTER_MARK_US))
return false;
dst = data; dst = data;
return true; return true;
} }
@ -71,15 +90,24 @@ static bool decode_data(RemoteReceiveData &src, CoolixData &dst) {
} }
optional<CoolixData> CoolixProtocol::decode(RemoteReceiveData data) { optional<CoolixData> CoolixProtocol::decode(RemoteReceiveData data) {
CoolixData first, second; CoolixData result;
if (data.expect_item(HEADER_MARK_US, HEADER_SPACE_US) && decode_data(data, first) && const auto size = data.size();
data.expect_item(FOOTER_MARK_US, FOOTER_SPACE_US) && data.expect_item(HEADER_MARK_US, HEADER_SPACE_US) && if ((size != 200 && size != 100) || !decode_frame(data, result.first))
decode_data(data, second) && data.expect_mark(FOOTER_MARK_US) && first == second) return {};
return first; if (size == 100 || !data.expect_space(FOOTER_SPACE_US) || !decode_frame(data, result.second))
return {}; result.second = 0;
return result;
} }
void CoolixProtocol::dump(const CoolixData &data) { ESP_LOGD(TAG, "Received Coolix: 0x%06X", data); } void CoolixProtocol::dump(const CoolixData &data) {
if (data.is_strict()) {
ESP_LOGD(TAG, "Received Coolix: 0x%06X", data.first);
} else if (data.has_second()) {
ESP_LOGD(TAG, "Received unstrict Coolix: [0x%06X, 0x%06X]", data.first, data.second);
} else {
ESP_LOGD(TAG, "Received unstrict Coolix: [0x%06X]", data.first);
}
}
} // namespace remote_base } // namespace remote_base
} // namespace esphome } // namespace esphome

View file

@ -7,7 +7,16 @@
namespace esphome { namespace esphome {
namespace remote_base { namespace remote_base {
using CoolixData = uint32_t; struct CoolixData {
CoolixData() {}
CoolixData(uint32_t a) : first(a), second(a) {}
CoolixData(uint32_t a, uint32_t b) : first(a), second(b) {}
bool operator==(const CoolixData &other) const;
bool is_strict() const { return this->first == this->second; }
bool has_second() const { return this->second != 0; }
uint32_t first;
uint32_t second;
};
class CoolixProtocol : public RemoteProtocol<CoolixData> { class CoolixProtocol : public RemoteProtocol<CoolixData> {
public: public:
@ -19,10 +28,10 @@ class CoolixProtocol : public RemoteProtocol<CoolixData> {
DECLARE_REMOTE_PROTOCOL(Coolix) DECLARE_REMOTE_PROTOCOL(Coolix)
template<typename... Ts> class CoolixAction : public RemoteTransmitterActionBase<Ts...> { template<typename... Ts> class CoolixAction : public RemoteTransmitterActionBase<Ts...> {
TEMPLATABLE_VALUE(CoolixData, data) TEMPLATABLE_VALUE(uint32_t, first)
TEMPLATABLE_VALUE(uint32_t, second)
void encode(RemoteTransmitData *dst, Ts... x) override { void encode(RemoteTransmitData *dst, Ts... x) override {
CoolixData data = this->data_.value(x...); CoolixProtocol().encode(dst, {this->first_.value(x...), this->second_.value(x...)});
CoolixProtocol().encode(dst, data);
} }
}; };

View file

@ -57,6 +57,7 @@ from esphome.const import (
DEVICE_CLASS_NITROGEN_MONOXIDE, DEVICE_CLASS_NITROGEN_MONOXIDE,
DEVICE_CLASS_NITROUS_OXIDE, DEVICE_CLASS_NITROUS_OXIDE,
DEVICE_CLASS_OZONE, DEVICE_CLASS_OZONE,
DEVICE_CLASS_PH,
DEVICE_CLASS_PM1, DEVICE_CLASS_PM1,
DEVICE_CLASS_PM10, DEVICE_CLASS_PM10,
DEVICE_CLASS_PM25, DEVICE_CLASS_PM25,
@ -114,6 +115,7 @@ DEVICE_CLASSES = [
DEVICE_CLASS_NITROGEN_MONOXIDE, DEVICE_CLASS_NITROGEN_MONOXIDE,
DEVICE_CLASS_NITROUS_OXIDE, DEVICE_CLASS_NITROUS_OXIDE,
DEVICE_CLASS_OZONE, DEVICE_CLASS_OZONE,
DEVICE_CLASS_PH,
DEVICE_CLASS_PM1, DEVICE_CLASS_PM1,
DEVICE_CLASS_PM10, DEVICE_CLASS_PM10,
DEVICE_CLASS_PM25, DEVICE_CLASS_PM25,

View file

@ -0,0 +1,57 @@
#include "sigma_delta_output.h"
#include "esphome/core/log.h"
namespace esphome {
namespace sigma_delta_output {
static const char *const TAG = "output.sigma_delta";
void SigmaDeltaOutput::setup() {
if (this->pin_)
this->pin_->setup();
}
void SigmaDeltaOutput::dump_config() {
ESP_LOGCONFIG(TAG, "Sigma Delta Output:");
LOG_PIN(" Pin: ", this->pin_);
if (this->state_change_trigger_) {
ESP_LOGCONFIG(TAG, " State change automation configured");
}
if (this->turn_on_trigger_) {
ESP_LOGCONFIG(TAG, " Turn on automation configured");
}
if (this->turn_off_trigger_) {
ESP_LOGCONFIG(TAG, " Turn off automation configured");
}
LOG_UPDATE_INTERVAL(this);
LOG_FLOAT_OUTPUT(this);
}
void SigmaDeltaOutput::update() {
this->accum_ += this->state_;
const bool next_value = this->accum_ > 0;
if (next_value) {
this->accum_ -= 1.;
}
if (next_value != this->value_) {
this->value_ = next_value;
if (this->pin_) {
this->pin_->digital_write(next_value);
}
if (this->state_change_trigger_) {
this->state_change_trigger_->trigger(next_value);
}
if (next_value && this->turn_on_trigger_) {
this->turn_on_trigger_->trigger();
} else if (!next_value && this->turn_off_trigger_) {
this->turn_off_trigger_->trigger();
}
}
}
} // namespace sigma_delta_output
} // namespace esphome

View file

@ -1,9 +1,12 @@
#pragma once #pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/output/float_output.h" #include "esphome/components/output/float_output.h"
namespace esphome { namespace esphome {
namespace sigma_delta_output { namespace sigma_delta_output {
class SigmaDeltaOutput : public PollingComponent, public output::FloatOutput { class SigmaDeltaOutput : public PollingComponent, public output::FloatOutput {
public: public:
Trigger<> *get_turn_on_trigger() { Trigger<> *get_turn_on_trigger() {
@ -25,31 +28,9 @@ class SigmaDeltaOutput : public PollingComponent, public output::FloatOutput {
void set_pin(GPIOPin *pin) { this->pin_ = pin; }; void set_pin(GPIOPin *pin) { this->pin_ = pin; };
void write_state(float state) override { this->state_ = state; } void write_state(float state) override { this->state_ = state; }
void update() override { void setup() override;
this->accum_ += this->state_; void dump_config() override;
const bool next_value = this->accum_ > 0; void update() override;
if (next_value) {
this->accum_ -= 1.;
}
if (next_value != this->value_) {
this->value_ = next_value;
if (this->pin_) {
this->pin_->digital_write(next_value);
}
if (this->state_change_trigger_) {
this->state_change_trigger_->trigger(next_value);
}
if (next_value && this->turn_on_trigger_) {
this->turn_on_trigger_->trigger();
} else if (!next_value && this->turn_off_trigger_) {
this->turn_off_trigger_->trigger();
}
}
}
protected: protected:
GPIOPin *pin_{nullptr}; GPIOPin *pin_{nullptr};

View file

@ -130,7 +130,7 @@ void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) {
void VoiceAssistant::request_start(bool continuous) { void VoiceAssistant::request_start(bool continuous) {
ESP_LOGD(TAG, "Requesting start..."); ESP_LOGD(TAG, "Requesting start...");
if (!api::global_api_server->start_voice_assistant(this->conversation_id_)) { if (!api::global_api_server->start_voice_assistant(this->conversation_id_, this->silence_detection_)) {
ESP_LOGW(TAG, "Could not request start."); ESP_LOGW(TAG, "Could not request start.");
this->error_trigger_->trigger("not-connected", "Could not request start."); this->error_trigger_->trigger("not-connected", "Could not request start.");
this->continuous_ = false; this->continuous_ = false;

View file

@ -25,10 +25,9 @@ namespace voice_assistant {
// Version 1: Initial version // Version 1: Initial version
// Version 2: Adds raw speaker support // Version 2: Adds raw speaker support
// Version 3: Adds continuous support // Version 3: Unused/skip
static const uint32_t INITIAL_VERSION = 1; static const uint32_t INITIAL_VERSION = 1;
static const uint32_t SPEAKER_SUPPORT = 2; static const uint32_t SPEAKER_SUPPORT = 2;
static const uint32_t SILENCE_DETECTION_SUPPORT = 3;
class VoiceAssistant : public Component { class VoiceAssistant : public Component {
public: public:
@ -48,9 +47,6 @@ class VoiceAssistant : public Component {
uint32_t get_version() const { uint32_t get_version() const {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_ != nullptr) { if (this->speaker_ != nullptr) {
if (this->silence_detection_) {
return SILENCE_DETECTION_SUPPORT;
}
return SPEAKER_SUPPORT; return SPEAKER_SUPPORT;
} }
#endif #endif

View file

@ -988,6 +988,7 @@ DEVICE_CLASS_OCCUPANCY = "occupancy"
DEVICE_CLASS_OPENING = "opening" DEVICE_CLASS_OPENING = "opening"
DEVICE_CLASS_OUTLET = "outlet" DEVICE_CLASS_OUTLET = "outlet"
DEVICE_CLASS_OZONE = "ozone" DEVICE_CLASS_OZONE = "ozone"
DEVICE_CLASS_PH = "ph"
DEVICE_CLASS_PLUG = "plug" DEVICE_CLASS_PLUG = "plug"
DEVICE_CLASS_PM1 = "pm1" DEVICE_CLASS_PM1 = "pm1"
DEVICE_CLASS_PM10 = "pm10" DEVICE_CLASS_PM10 = "pm10"

View file

@ -475,6 +475,7 @@ template<typename... Ts> class CallbackManager<void(Ts...)> {
for (auto &cb : this->callbacks_) for (auto &cb : this->callbacks_)
cb(args...); cb(args...);
} }
size_t size() const { return this->callbacks_.size(); }
/// Call all callbacks in this manager. /// Call all callbacks in this manager.
void operator()(Ts... args) { call(args...); } void operator()(Ts... args) { call(args...); }

View file

@ -25,6 +25,7 @@ import tornado.ioloop
import tornado.iostream import tornado.iostream
import tornado.netutil import tornado.netutil
import tornado.process import tornado.process
import tornado.queues
import tornado.web import tornado.web
import tornado.websocket import tornado.websocket
import yaml import yaml
@ -92,6 +93,10 @@ class DashboardSettings:
def using_auth(self): def using_auth(self):
return self.using_password or self.using_ha_addon_auth return self.using_password or self.using_ha_addon_auth
@property
def streamer_mode(self):
return get_bool_env("ESPHOME_STREAMER_MODE")
def check_password(self, username, password): def check_password(self, username, password):
if not self.using_auth: if not self.using_auth:
return True return True
@ -130,7 +135,7 @@ def template_args():
"docs_link": docs_link, "docs_link": docs_link,
"get_static_file_url": get_static_file_url, "get_static_file_url": get_static_file_url,
"relative_url": settings.relative_url, "relative_url": settings.relative_url,
"streamer_mode": get_bool_env("ESPHOME_STREAMER_MODE"), "streamer_mode": settings.streamer_mode,
"config_dir": settings.config_dir, "config_dir": settings.config_dir,
} }
@ -202,7 +207,11 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
def __init__(self, application, request, **kwargs): def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs) super().__init__(application, request, **kwargs)
self._proc = None self._proc = None
self._queue = None
self._is_closed = False self._is_closed = False
# Windows doesn't support non-blocking pipes,
# use Popen() with a reading thread instead
self._use_popen = os.name == "nt"
@authenticated @authenticated
def on_message(self, message): def on_message(self, message):
@ -224,13 +233,28 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
return return
command = self.build_command(json_message) command = self.build_command(json_message)
_LOGGER.info("Running command '%s'", " ".join(shlex_quote(x) for x in command)) _LOGGER.info("Running command '%s'", " ".join(shlex_quote(x) for x in command))
self._proc = tornado.process.Subprocess(
command, if self._use_popen:
stdout=tornado.process.Subprocess.STREAM, self._queue = tornado.queues.Queue()
stderr=subprocess.STDOUT, # pylint: disable=consider-using-with
stdin=tornado.process.Subprocess.STREAM, self._proc = subprocess.Popen(
) command,
self._proc.set_exit_callback(self._proc_on_exit) stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
stdout_thread = threading.Thread(target=self._stdout_thread)
stdout_thread.daemon = True
stdout_thread.start()
else:
self._proc = tornado.process.Subprocess(
command,
stdout=tornado.process.Subprocess.STREAM,
stderr=subprocess.STDOUT,
stdin=tornado.process.Subprocess.STREAM,
)
self._proc.set_exit_callback(self._proc_on_exit)
tornado.ioloop.IOLoop.current().spawn_callback(self._redirect_stdout) tornado.ioloop.IOLoop.current().spawn_callback(self._redirect_stdout)
@property @property
@ -252,7 +276,13 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
while True: while True:
try: try:
data = yield self._proc.stdout.read_until_regex(reg) if self._use_popen:
data = yield self._queue.get()
if data is None:
self._proc_on_exit(self._proc.poll())
break
else:
data = yield self._proc.stdout.read_until_regex(reg)
except tornado.iostream.StreamClosedError: except tornado.iostream.StreamClosedError:
break break
data = codecs.decode(data, "utf8", "replace") data = codecs.decode(data, "utf8", "replace")
@ -260,6 +290,19 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
_LOGGER.debug("> stdout: %s", data) _LOGGER.debug("> stdout: %s", data)
self.write_message({"event": "line", "data": data}) self.write_message({"event": "line", "data": data})
def _stdout_thread(self):
if not self._use_popen:
return
while True:
data = self._proc.stdout.readline()
if data:
data = data.replace(b"\r", b"")
self._queue.put_nowait(data)
if self._proc.poll() is not None:
break
self._proc.wait(1.0)
self._queue.put_nowait(None)
def _proc_on_exit(self, returncode): def _proc_on_exit(self, returncode):
if not self._is_closed: if not self._is_closed:
# Check if the proc was not forcibly closed # Check if the proc was not forcibly closed
@ -270,7 +313,10 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
# Check if proc exists (if 'start' has been run) # Check if proc exists (if 'start' has been run)
if self.is_process_active: if self.is_process_active:
_LOGGER.debug("Terminating process") _LOGGER.debug("Terminating process")
self._proc.proc.terminate() if self._use_popen:
self._proc.terminate()
else:
self._proc.proc.terminate()
# Shutdown proc on WS close # Shutdown proc on WS close
self._is_closed = True self._is_closed = True
@ -354,7 +400,10 @@ class EsphomeCompileHandler(EsphomeCommandWebSocket):
class EsphomeValidateHandler(EsphomeCommandWebSocket): class EsphomeValidateHandler(EsphomeCommandWebSocket):
def build_command(self, json_message): def build_command(self, json_message):
config_file = settings.rel_path(json_message["configuration"]) config_file = settings.rel_path(json_message["configuration"])
return ["esphome", "--dashboard", "config", config_file] command = ["esphome", "--dashboard", "config", config_file]
if not settings.streamer_mode:
command.append("--show-secrets")
return command
class EsphomeCleanMqttHandler(EsphomeCommandWebSocket): class EsphomeCleanMqttHandler(EsphomeCommandWebSocket):
@ -1105,7 +1154,7 @@ class JsonConfigRequestHandler(BaseHandler):
self.send_error(404) self.send_error(404)
return return
args = ["esphome", "config", settings.rel_path(configuration), "--show-secrets"] args = ["esphome", "config", filename, "--show-secrets"]
rc, stdout, _ = run_system_command(*args) rc, stdout, _ = run_system_command(*args)

View file

@ -15,6 +15,7 @@ _LOGGER = logging.getLogger(__name__)
def run_git_command(cmd, cwd=None) -> str: def run_git_command(cmd, cwd=None) -> str:
_LOGGER.debug("Running git command: %s", " ".join(cmd))
try: try:
ret = subprocess.run(cmd, cwd=cwd, capture_output=True, check=False) ret = subprocess.run(cmd, cwd=cwd, capture_output=True, check=False)
except FileNotFoundError as err: except FileNotFoundError as err:
@ -48,6 +49,7 @@ def clone_or_update(
domain: str, domain: str,
username: str = None, username: str = None,
password: str = None, password: str = None,
submodules: Optional[list[str]] = None,
) -> tuple[Path, Optional[Callable[[], None]]]: ) -> tuple[Path, Optional[Callable[[], None]]]:
key = f"{url}@{ref}" key = f"{url}@{ref}"
@ -74,6 +76,14 @@ def clone_or_update(
run_git_command(["git", "fetch", "--", "origin", ref], str(repo_dir)) run_git_command(["git", "fetch", "--", "origin", ref], str(repo_dir))
run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir)) run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir))
if submodules is not None:
_LOGGER.info(
"Initialising submodules (%s) for %s", ", ".join(submodules), key
)
run_git_command(
["git", "submodule", "update", "--init"] + submodules, str(repo_dir)
)
else: else:
# Check refresh needed # Check refresh needed
file_timestamp = Path(repo_dir / ".git" / "FETCH_HEAD") file_timestamp = Path(repo_dir / ".git" / "FETCH_HEAD")
@ -97,6 +107,14 @@ def clone_or_update(
# Hard reset to FETCH_HEAD (short-lived git ref corresponding to most recent fetch) # Hard reset to FETCH_HEAD (short-lived git ref corresponding to most recent fetch)
run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir)) run_git_command(["git", "reset", "--hard", "FETCH_HEAD"], str(repo_dir))
if submodules is not None:
_LOGGER.info(
"Updating submodules (%s) for %s", ", ".join(submodules), key
)
run_git_command(
["git", "submodule", "update", "--init"] + submodules, str(repo_dir)
)
def revert(): def revert():
_LOGGER.info("Reverting changes to %s -> %s", key, old_sha) _LOGGER.info("Reverting changes to %s -> %s", key, old_sha)
run_git_command(["git", "reset", "--hard", old_sha], str(repo_dir)) run_git_command(["git", "reset", "--hard", old_sha], str(repo_dir))

View file

@ -71,6 +71,8 @@ def setup_log(
) -> None: ) -> None:
import colorama import colorama
colorama.init()
if debug: if debug:
log_level = logging.DEBUG log_level = logging.DEBUG
CORE.verbose = True CORE.verbose = True
@ -82,7 +84,6 @@ def setup_log(
logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING)
colorama.init()
logging.getLogger().handlers[0].setFormatter( logging.getLogger().handlers[0].setFormatter(
ESPHomeLogFormatter(include_timestamp=include_timestamp) ESPHomeLogFormatter(include_timestamp=include_timestamp)
) )

View file

@ -105,7 +105,7 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script
; This are common settings for the ESP32 (all variants) using Arduino. ; This are common settings for the ESP32 (all variants) using Arduino.
[common:esp32-arduino] [common:esp32-arduino]
extends = common:arduino extends = common:arduino
platform = platformio/espressif32@5.3.0 platform = platformio/espressif32@5.4.0
platform_packages = platform_packages =
platformio/framework-arduinoespressif32@~3.20005.0 platformio/framework-arduinoespressif32@~3.20005.0
@ -134,9 +134,9 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script
; This are common settings for the ESP32 (all variants) using IDF. ; This are common settings for the ESP32 (all variants) using IDF.
[common:esp32-idf] [common:esp32-idf]
extends = common:idf extends = common:idf
platform = platformio/espressif32@5.3.0 platform = platformio/espressif32@5.4.0
platform_packages = platform_packages =
platformio/framework-espidf@~3.40404.0 platformio/framework-espidf@~3.40405.0
framework = espidf framework = espidf
lib_deps = lib_deps =

View file

@ -1,5 +1,5 @@
voluptuous==0.13.1 voluptuous==0.13.1
PyYAML==6.0 PyYAML==6.0.1
paho-mqtt==1.6.1 paho-mqtt==1.6.1
colorama==0.4.6 colorama==0.4.6
tornado==6.3.2 tornado==6.3.2
@ -8,7 +8,7 @@ tzdata>=2021.1 # from time
pyserial==3.5 pyserial==3.5
platformio==6.1.7 # When updating platformio, also update Dockerfile platformio==6.1.7 # When updating platformio, also update Dockerfile
esptool==4.6.2 esptool==4.6.2
click==8.1.3 click==8.1.5
esphome-dashboard==20230711.0 esphome-dashboard==20230711.0
aioesphomeapi==15.0.0 aioesphomeapi==15.0.0
zeroconf==0.69.0 zeroconf==0.69.0

View file

@ -1609,6 +1609,18 @@ binary_sensor:
-2267, -2267,
1709, 1709,
] ]
- platform: remote_receiver
name: Coolix Test 1
coolix: 0xB21F98
- platform: remote_receiver
name: Coolix Test 2
coolix:
first: 0xB2E003
- platform: remote_receiver
name: Coolix Test 3
coolix:
first: 0xB2E003
second: 0xB21F98
- platform: as3935 - platform: as3935
name: Storm Alert name: Storm Alert
- platform: analog_threshold - platform: analog_threshold
@ -2283,8 +2295,16 @@ switch:
- platform: template - platform: template
name: MIDEA_RAW name: MIDEA_RAW
turn_on_action: turn_on_action:
remote_transmitter.transmit_midea: - remote_transmitter.transmit_coolix:
code: [0xA2, 0x08, 0xFF, 0xFF, 0xFF] first: 0xB21F98
- remote_transmitter.transmit_coolix:
first: 0xB21F98
second: 0xB21F98
- remote_transmitter.transmit_coolix:
first: !lambda "return 0xB21F98;"
second: !lambda "return 0xB21F98;"
- remote_transmitter.transmit_midea:
code: [0xA2, 0x08, 0xFF, 0xFF, 0xFF]
- platform: gpio - platform: gpio
name: "MCP23S08 Pin #0" name: "MCP23S08 Pin #0"
pin: pin:
@ -2868,6 +2888,9 @@ tm1651:
remote_receiver: remote_receiver:
pin: GPIO32 pin: GPIO32
dump: all dump: all
on_coolix:
then:
delay: !lambda "return x.first + x.second;"
status_led: status_led:
pin: GPIO2 pin: GPIO2