mirror of
https://github.com/esphome/esphome.git
synced 2024-12-26 07:24:54 +01:00
commit
cc115e7cc9
16 changed files with 109 additions and 52 deletions
|
@ -7,6 +7,8 @@ CODEOWNERS = ["@ellull"]
|
|||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
MULTI_CONF = True
|
||||
|
||||
CONF_PWM = "pwm"
|
||||
CONF_DIVIDER = "divider"
|
||||
CONF_DAC = "dac"
|
||||
|
|
|
@ -252,7 +252,9 @@ ThrottleAverageFilter::ThrottleAverageFilter(uint32_t time_period) : time_period
|
|||
|
||||
optional<float> ThrottleAverageFilter::new_value(float value) {
|
||||
ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::new_value(value=%f)", this, value);
|
||||
if (!std::isnan(value)) {
|
||||
if (std::isnan(value)) {
|
||||
this->have_nan_ = true;
|
||||
} else {
|
||||
this->sum_ += value;
|
||||
this->n_++;
|
||||
}
|
||||
|
@ -262,12 +264,14 @@ void ThrottleAverageFilter::setup() {
|
|||
this->set_interval("throttle_average", this->time_period_, [this]() {
|
||||
ESP_LOGVV(TAG, "ThrottleAverageFilter(%p)::interval(sum=%f, n=%i)", this, this->sum_, this->n_);
|
||||
if (this->n_ == 0) {
|
||||
this->output(NAN);
|
||||
if (this->have_nan_)
|
||||
this->output(NAN);
|
||||
} else {
|
||||
this->output(this->sum_ / this->n_);
|
||||
this->sum_ = 0.0f;
|
||||
this->n_ = 0;
|
||||
}
|
||||
this->have_nan_ = false;
|
||||
});
|
||||
}
|
||||
float ThrottleAverageFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
|
|
@ -245,6 +245,7 @@ class ThrottleAverageFilter : public Filter, public Component {
|
|||
uint32_t time_period_;
|
||||
float sum_{0.0f};
|
||||
unsigned int n_{0};
|
||||
bool have_nan_{false};
|
||||
};
|
||||
|
||||
using lambda_filter_t = std::function<optional<float>(float)>;
|
||||
|
|
|
@ -19,7 +19,7 @@ SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan)
|
|||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpeedFan),
|
||||
cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(CONF_SPEED): cv.invalid(
|
||||
|
@ -32,11 +32,14 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
|||
|
||||
|
||||
async def to_code(config):
|
||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], output_, config[CONF_SPEED_COUNT])
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_SPEED_COUNT])
|
||||
await cg.register_component(var, config)
|
||||
await fan.register_fan(var, config)
|
||||
|
||||
if CONF_OUTPUT in config:
|
||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(output_))
|
||||
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
cg.add(var.set_oscillating(oscillation_output))
|
||||
|
|
|
@ -36,9 +36,10 @@ void SpeedFan::control(const fan::FanCall &call) {
|
|||
}
|
||||
|
||||
void SpeedFan::write_state_() {
|
||||
float speed = this->state ? static_cast<float>(this->speed) / static_cast<float>(this->speed_count_) : 0.0f;
|
||||
this->output_->set_level(speed);
|
||||
|
||||
if (this->output_ != nullptr) {
|
||||
float speed = this->state ? static_cast<float>(this->speed) / static_cast<float>(this->speed_count_) : 0.0f;
|
||||
this->output_->set_level(speed);
|
||||
}
|
||||
if (this->oscillating_ != nullptr)
|
||||
this->oscillating_->set_state(this->oscillating);
|
||||
if (this->direction_ != nullptr)
|
||||
|
|
|
@ -12,9 +12,10 @@ namespace speed {
|
|||
|
||||
class SpeedFan : public Component, public fan::Fan {
|
||||
public:
|
||||
SpeedFan(output::FloatOutput *output, int speed_count) : output_(output), speed_count_(speed_count) {}
|
||||
SpeedFan(int speed_count) : speed_count_(speed_count) {}
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void set_output(output::FloatOutput *output) { this->output_ = output; }
|
||||
void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
|
||||
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
|
||||
void set_preset_modes(const std::set<std::string> &presets) { this->preset_modes_ = presets; }
|
||||
|
@ -24,7 +25,7 @@ class SpeedFan : public Component, public fan::Fan {
|
|||
void control(const fan::FanCall &call) override;
|
||||
void write_state_();
|
||||
|
||||
output::FloatOutput *output_;
|
||||
output::FloatOutput *output_{nullptr};
|
||||
output::BinaryOutput *oscillating_{nullptr};
|
||||
output::BinaryOutput *direction_{nullptr};
|
||||
int speed_count_{};
|
||||
|
|
|
@ -74,7 +74,8 @@ CONF_FORCE_SW = "force_sw"
|
|||
CONF_INTERFACE = "interface"
|
||||
CONF_INTERFACE_INDEX = "interface_index"
|
||||
|
||||
# RP2040 SPI pin assignments are complicated. Refer to https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
|
||||
# RP2040 SPI pin assignments are complicated;
|
||||
# refer to GPIO function select table in https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
|
||||
|
||||
RP_SPI_PINSETS = [
|
||||
{
|
||||
|
@ -85,7 +86,7 @@ RP_SPI_PINSETS = [
|
|||
{
|
||||
CONF_MISO_PIN: [8, 12, 24, 28, -1],
|
||||
CONF_CLK_PIN: [10, 14, 26],
|
||||
CONF_MOSI_PIN: [11, 23, 27, -1],
|
||||
CONF_MOSI_PIN: [11, 15, 27, -1],
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "thermostat_climate.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace thermostat {
|
||||
|
@ -63,6 +62,15 @@ void ThermostatClimate::setup() {
|
|||
this->publish_state();
|
||||
}
|
||||
|
||||
void ThermostatClimate::loop() {
|
||||
for (auto &timer : this->timer_) {
|
||||
if (timer.active && (timer.started + timer.time < millis())) {
|
||||
timer.active = false;
|
||||
timer.func();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float ThermostatClimate::cool_deadband() { return this->cooling_deadband_; }
|
||||
float ThermostatClimate::cool_overrun() { return this->cooling_overrun_; }
|
||||
float ThermostatClimate::heat_deadband() { return this->heating_deadband_; }
|
||||
|
@ -439,6 +447,7 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
|
|||
this->start_timer_(thermostat::TIMER_FANNING_ON);
|
||||
trig_fan = this->fan_only_action_trigger_;
|
||||
}
|
||||
this->cooling_max_runtime_exceeded_ = false;
|
||||
trig = this->cool_action_trigger_;
|
||||
ESP_LOGVV(TAG, "Switching to COOLING action");
|
||||
action_ready = true;
|
||||
|
@ -452,6 +461,7 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu
|
|||
this->start_timer_(thermostat::TIMER_FANNING_ON);
|
||||
trig_fan = this->fan_only_action_trigger_;
|
||||
}
|
||||
this->heating_max_runtime_exceeded_ = false;
|
||||
trig = this->heat_action_trigger_;
|
||||
ESP_LOGVV(TAG, "Switching to HEATING action");
|
||||
action_ready = true;
|
||||
|
@ -752,15 +762,15 @@ bool ThermostatClimate::heating_action_ready_() {
|
|||
|
||||
void ThermostatClimate::start_timer_(const ThermostatClimateTimerIndex timer_index) {
|
||||
if (this->timer_duration_(timer_index) > 0) {
|
||||
this->set_timeout(this->timer_[timer_index].name, this->timer_duration_(timer_index),
|
||||
this->timer_cbf_(timer_index));
|
||||
this->timer_[timer_index].started = millis();
|
||||
this->timer_[timer_index].active = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ThermostatClimate::cancel_timer_(ThermostatClimateTimerIndex timer_index) {
|
||||
auto ret = this->timer_[timer_index].active;
|
||||
this->timer_[timer_index].active = false;
|
||||
return this->cancel_timeout(this->timer_[timer_index].name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ThermostatClimate::timer_active_(ThermostatClimateTimerIndex timer_index) {
|
||||
|
@ -777,7 +787,6 @@ std::function<void()> ThermostatClimate::timer_cbf_(ThermostatClimateTimerIndex
|
|||
|
||||
void ThermostatClimate::cooling_max_run_time_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "cooling_max_run_time timer expired");
|
||||
this->timer_[thermostat::TIMER_COOLING_MAX_RUN_TIME].active = false;
|
||||
this->cooling_max_runtime_exceeded_ = true;
|
||||
this->trigger_supplemental_action_();
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
|
@ -785,21 +794,18 @@ void ThermostatClimate::cooling_max_run_time_timer_callback_() {
|
|||
|
||||
void ThermostatClimate::cooling_off_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "cooling_off timer expired");
|
||||
this->timer_[thermostat::TIMER_COOLING_OFF].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::cooling_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "cooling_on timer expired");
|
||||
this->timer_[thermostat::TIMER_COOLING_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::fan_mode_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "fan_mode timer expired");
|
||||
this->timer_[thermostat::TIMER_FAN_MODE].active = false;
|
||||
this->switch_to_fan_mode_(this->fan_mode.value_or(climate::CLIMATE_FAN_ON));
|
||||
if (this->supports_fan_only_action_uses_fan_mode_timer_)
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
|
@ -807,19 +813,16 @@ void ThermostatClimate::fan_mode_timer_callback_() {
|
|||
|
||||
void ThermostatClimate::fanning_off_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "fanning_off timer expired");
|
||||
this->timer_[thermostat::TIMER_FANNING_OFF].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::fanning_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "fanning_on timer expired");
|
||||
this->timer_[thermostat::TIMER_FANNING_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::heating_max_run_time_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "heating_max_run_time timer expired");
|
||||
this->timer_[thermostat::TIMER_HEATING_MAX_RUN_TIME].active = false;
|
||||
this->heating_max_runtime_exceeded_ = true;
|
||||
this->trigger_supplemental_action_();
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
|
@ -827,21 +830,18 @@ void ThermostatClimate::heating_max_run_time_timer_callback_() {
|
|||
|
||||
void ThermostatClimate::heating_off_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "heating_off timer expired");
|
||||
this->timer_[thermostat::TIMER_HEATING_OFF].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::heating_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "heating_on timer expired");
|
||||
this->timer_[thermostat::TIMER_HEATING_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
||||
void ThermostatClimate::idle_on_timer_callback_() {
|
||||
ESP_LOGVV(TAG, "idle_on timer expired");
|
||||
this->timer_[thermostat::TIMER_IDLE_ON].active = false;
|
||||
this->switch_to_action_(this->compute_action_());
|
||||
this->switch_to_supplemental_action_(this->compute_supplemental_action_());
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
|
@ -26,9 +28,9 @@ enum ThermostatClimateTimerIndex : size_t {
|
|||
|
||||
enum OnBootRestoreFrom : size_t { MEMORY = 0, DEFAULT_PRESET = 1 };
|
||||
struct ThermostatClimateTimer {
|
||||
const std::string name;
|
||||
bool active;
|
||||
uint32_t time;
|
||||
uint32_t started;
|
||||
std::function<void()> func;
|
||||
};
|
||||
|
||||
|
@ -59,6 +61,7 @@ class ThermostatClimate : public climate::Climate, public Component {
|
|||
ThermostatClimate();
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
||||
void set_default_preset(const std::string &custom_preset);
|
||||
void set_default_preset(climate::ClimatePreset preset);
|
||||
|
@ -441,16 +444,17 @@ class ThermostatClimate : public climate::Climate, public Component {
|
|||
|
||||
/// Climate action timers
|
||||
std::vector<ThermostatClimateTimer> timer_{
|
||||
{"cool_run", false, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)},
|
||||
{"cool_off", false, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)},
|
||||
{"cool_on", false, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)},
|
||||
{"fan_mode", false, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)},
|
||||
{"fan_off", false, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)},
|
||||
{"fan_on", false, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)},
|
||||
{"heat_run", false, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)},
|
||||
{"heat_off", false, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)},
|
||||
{"heat_on", false, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)},
|
||||
{"idle_on", false, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)}};
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::cooling_off_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::cooling_on_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::fan_mode_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::fanning_off_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::fanning_on_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::heating_max_run_time_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::heating_off_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::heating_on_timer_callback_, this)},
|
||||
{false, 0, 0, std::bind(&ThermostatClimate::idle_on_timer_callback_, this)},
|
||||
};
|
||||
|
||||
/// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc)
|
||||
std::map<climate::ClimatePreset, ThermostatClimateTargetTempConfig> preset_config_{};
|
||||
|
|
|
@ -785,6 +785,8 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
|||
obj->position, start_config);
|
||||
root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
|
||||
|
||||
if (obj->get_traits().get_supports_position())
|
||||
root["position"] = obj->position;
|
||||
if (obj->get_traits().get_supports_tilt())
|
||||
root["tilt"] = obj->tilt;
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2024.2.0"
|
||||
__version__ = "2024.2.1"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||
|
|
|
@ -8,3 +8,5 @@ MAX_EXECUTOR_WORKERS = 48
|
|||
|
||||
|
||||
SENTINEL = object()
|
||||
|
||||
DASHBOARD_COMMAND = ["esphome", "--dashboard"]
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import logging
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
from typing import TYPE_CHECKING, Any, Callable
|
||||
from collections.abc import Coroutine
|
||||
|
||||
from ..zeroconf import DiscoveredImport
|
||||
from .dns import DNSCache
|
||||
|
@ -71,6 +73,7 @@ class ESPHomeDashboard:
|
|||
"mdns_status",
|
||||
"settings",
|
||||
"dns_cache",
|
||||
"_background_tasks",
|
||||
)
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
@ -85,6 +88,7 @@ class ESPHomeDashboard:
|
|||
self.mdns_status: MDNSStatus | None = None
|
||||
self.settings = DashboardSettings()
|
||||
self.dns_cache = DNSCache()
|
||||
self._background_tasks: set[asyncio.Task] = set()
|
||||
|
||||
async def async_setup(self) -> None:
|
||||
"""Setup the dashboard."""
|
||||
|
@ -132,7 +136,19 @@ class ESPHomeDashboard:
|
|||
if settings.status_use_mqtt:
|
||||
status_thread_mqtt.join()
|
||||
self.mqtt_ping_request.set()
|
||||
for task in self._background_tasks:
|
||||
task.cancel()
|
||||
with contextlib.suppress(asyncio.CancelledError):
|
||||
await task
|
||||
await asyncio.sleep(0)
|
||||
|
||||
def async_create_background_task(
|
||||
self, coro: Coroutine[Any, Any, Any]
|
||||
) -> asyncio.Task:
|
||||
"""Create a background task."""
|
||||
task = self.loop.create_task(coro)
|
||||
task.add_done_callback(self._background_tasks.discard)
|
||||
return task
|
||||
|
||||
|
||||
DASHBOARD = ESPHomeDashboard()
|
||||
|
|
|
@ -10,12 +10,14 @@ from esphome import const, util
|
|||
from esphome.storage_json import StorageJSON, ext_storage_path
|
||||
|
||||
from .const import (
|
||||
DASHBOARD_COMMAND,
|
||||
EVENT_ENTRY_ADDED,
|
||||
EVENT_ENTRY_REMOVED,
|
||||
EVENT_ENTRY_STATE_CHANGED,
|
||||
EVENT_ENTRY_UPDATED,
|
||||
)
|
||||
from .enum import StrEnum
|
||||
from .util.subprocess import async_run_system_command
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .core import ESPHomeDashboard
|
||||
|
@ -235,6 +237,14 @@ class DashboardEntries:
|
|||
)
|
||||
return path_to_cache_key
|
||||
|
||||
def async_schedule_storage_json_update(self, filename: str) -> None:
|
||||
"""Schedule a task to update the storage JSON file."""
|
||||
self._dashboard.async_create_background_task(
|
||||
async_run_system_command(
|
||||
[*DASHBOARD_COMMAND, "compile", "--only-generate", filename]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class DashboardEntry:
|
||||
"""Represents a single dashboard entry.
|
||||
|
|
|
@ -9,11 +9,11 @@ import hashlib
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import secrets
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
||||
|
@ -40,6 +40,7 @@ from esphome.storage_json import StorageJSON, ext_storage_path, trash_storage_pa
|
|||
from esphome.util import get_serial_ports, shlex_quote
|
||||
from esphome.yaml_util import FastestAvailableSafeLoader
|
||||
|
||||
from .const import DASHBOARD_COMMAND
|
||||
from .core import DASHBOARD
|
||||
from .entries import EntryState, entry_state_to_bool
|
||||
from .util.file import write_file
|
||||
|
@ -286,9 +287,6 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
|
|||
raise NotImplementedError
|
||||
|
||||
|
||||
DASHBOARD_COMMAND = ["esphome", "--dashboard"]
|
||||
|
||||
|
||||
class EsphomePortCommandWebSocket(EsphomeCommandWebSocket):
|
||||
"""Base class for commands that require a port."""
|
||||
|
||||
|
@ -808,8 +806,16 @@ class EditRequestHandler(BaseHandler):
|
|||
@bind_config
|
||||
async def get(self, configuration: str | None = None) -> None:
|
||||
"""Get the content of a file."""
|
||||
loop = asyncio.get_running_loop()
|
||||
if not configuration.endswith((".yaml", ".yml")):
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
filename = settings.rel_path(configuration)
|
||||
if Path(filename).resolve().parent != settings.absolute_config_dir:
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
content = await loop.run_in_executor(
|
||||
None, self._read_file, filename, configuration
|
||||
)
|
||||
|
@ -835,15 +841,19 @@ class EditRequestHandler(BaseHandler):
|
|||
@bind_config
|
||||
async def post(self, configuration: str | None = None) -> None:
|
||||
"""Write the content of a file."""
|
||||
if not configuration.endswith((".yaml", ".yml")):
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
filename = settings.rel_path(configuration)
|
||||
if Path(filename).resolve().parent != settings.absolute_config_dir:
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
config_file = settings.rel_path(configuration)
|
||||
await loop.run_in_executor(
|
||||
None, self._write_file, config_file, self.request.body
|
||||
)
|
||||
await loop.run_in_executor(None, self._write_file, filename, self.request.body)
|
||||
# Ensure the StorageJSON is updated as well
|
||||
await async_run_system_command(
|
||||
[*DASHBOARD_COMMAND, "compile", "--only-generate", config_file]
|
||||
)
|
||||
DASHBOARD.entries.async_schedule_storage_json_update(filename)
|
||||
self.set_status(200)
|
||||
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ class DashboardImportDiscovery:
|
|||
self, zeroconf: Zeroconf, info: AsyncServiceInfo, service_type: str, name: str
|
||||
) -> None:
|
||||
"""Process a service info."""
|
||||
if await info.async_request(zeroconf):
|
||||
if await info.async_request(zeroconf, timeout=3000):
|
||||
self._process_service_info(name, info)
|
||||
|
||||
def _process_service_info(self, name: str, info: ServiceInfo) -> None:
|
||||
|
|
Loading…
Reference in a new issue