mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 17:27:59 +01:00
Merge branch 'dev' into optolink
This commit is contained in:
commit
d4a43157f3
86 changed files with 669 additions and 368 deletions
2
.github/workflows/ci-docker.yml
vendored
2
.github/workflows/ci-docker.yml
vendored
|
@ -11,6 +11,7 @@ on:
|
|||
- ".github/workflows/**"
|
||||
- "requirements*.txt"
|
||||
- "platformio.ini"
|
||||
- "script/platformio_install_deps.py"
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
|
@ -18,6 +19,7 @@ on:
|
|||
- ".github/workflows/**"
|
||||
- "requirements*.txt"
|
||||
- "platformio.ini"
|
||||
- "script/platformio_install_deps.py"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
|
@ -21,6 +21,7 @@ esphome/components/airthings_wave_mini/* @ncareau
|
|||
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||
esphome/components/am43/* @buxtronix
|
||||
esphome/components/am43/cover/* @buxtronix
|
||||
esphome/components/am43/sensor/* @buxtronix
|
||||
esphome/components/analog_threshold/* @ianchi
|
||||
esphome/components/animation/* @syndlex
|
||||
esphome/components/anova/* @buxtronix
|
||||
|
@ -162,6 +163,7 @@ esphome/components/midea/* @dudanov
|
|||
esphome/components/midea_ir/* @dudanov
|
||||
esphome/components/mitsubishi/* @RubyBailey
|
||||
esphome/components/mlx90393/* @functionpointer
|
||||
esphome/components/mlx90614/* @jesserockz
|
||||
esphome/components/mmc5603/* @benhoff
|
||||
esphome/components/modbus_controller/* @martgras
|
||||
esphome/components/modbus_controller/binary_sensor/* @martgras
|
||||
|
|
|
@ -24,6 +24,7 @@ RUN \
|
|||
python3-setuptools=52.0.0-4 \
|
||||
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
|
||||
python3-cryptography=3.3.2-1 \
|
||||
python3-venv=3.9.2-3 \
|
||||
iputils-ping=3:20210202-1 \
|
||||
git=1:2.30.2-1 \
|
||||
curl=7.74.0-1.3+deb11u7 \
|
||||
|
@ -59,7 +60,7 @@ RUN \
|
|||
|
||||
|
||||
# First install requirements to leverage caching when requirements don't change
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# This script is used in the docker containers to preinstall
|
||||
# all platformio libraries in the global storage
|
||||
|
||||
import configparser
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
|
||||
config.read(sys.argv[1])
|
||||
|
||||
libs = []
|
||||
# Extract from every lib_deps key in all sections
|
||||
for section in config.sections():
|
||||
conf = config[section]
|
||||
if "lib_deps" not in conf:
|
||||
continue
|
||||
for lib_dep in conf["lib_deps"].splitlines():
|
||||
if not lib_dep:
|
||||
# Empty line or comment
|
||||
continue
|
||||
if lib_dep.startswith("${"):
|
||||
# Extending from another section
|
||||
continue
|
||||
if "@" not in lib_dep:
|
||||
# No version pinned, this is an internal lib
|
||||
continue
|
||||
libs.append(lib_dep)
|
||||
|
||||
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
|
|
@ -0,0 +1 @@
|
|||
CODEOWNERS = ["@buxtronix"]
|
|
@ -5,7 +5,7 @@ from esphome.const import CONF_ID, CONF_PIN
|
|||
|
||||
CODEOWNERS = ["@buxtronix"]
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
AUTO_LOAD = ["am43", "sensor"]
|
||||
AUTO_LOAD = ["am43"]
|
||||
|
||||
CONF_INVERT_POSITION = "invert_position"
|
||||
|
||||
|
@ -27,10 +27,10 @@ CONFIG_SCHEMA = (
|
|||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_pin(config[CONF_PIN]))
|
||||
cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
|
||||
yield cg.register_component(var, config)
|
||||
yield cover.register_cover(var, config)
|
||||
yield ble_client.register_ble_node(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await cover.register_cover(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
|
|
|
@ -40,6 +40,7 @@ void Am43Component::loop() {
|
|||
|
||||
CoverTraits Am43Component::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_tilt(false);
|
||||
traits.set_is_assumed_state(false);
|
||||
|
|
|
@ -11,6 +11,7 @@ from esphome.const import (
|
|||
UNIT_PERCENT,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["am43"]
|
||||
CODEOWNERS = ["@buxtronix"]
|
||||
|
||||
am43_ns = cg.esphome_ns.namespace("am43")
|
||||
|
@ -38,15 +39,15 @@ CONFIG_SCHEMA = (
|
|||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield ble_client.register_ble_node(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
|
||||
if CONF_BATTERY_LEVEL in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||
sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||
cg.add(var.set_battery(sens))
|
||||
|
||||
if CONF_ILLUMINANCE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_ILLUMINANCE])
|
||||
sens = await sensor.new_sensor(config[CONF_ILLUMINANCE])
|
||||
cg.add(var.set_illuminance(sens))
|
|
@ -1,6 +1,6 @@
|
|||
#include "am43.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "am43_sensor.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
|
@ -288,6 +288,7 @@ message ListEntitiesCoverResponse {
|
|||
bool disabled_by_default = 9;
|
||||
string icon = 10;
|
||||
EntityCategory entity_category = 11;
|
||||
bool supports_stop = 12;
|
||||
}
|
||||
|
||||
enum LegacyCoverState {
|
||||
|
@ -861,8 +862,7 @@ message ClimateStateResponse {
|
|||
float target_temperature = 4;
|
||||
float target_temperature_low = 5;
|
||||
float target_temperature_high = 6;
|
||||
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
|
||||
bool legacy_away = 7;
|
||||
bool unused_legacy_away = 7;
|
||||
ClimateAction action = 8;
|
||||
ClimateFanMode fan_mode = 9;
|
||||
ClimateSwingMode swing_mode = 10;
|
||||
|
@ -885,9 +885,8 @@ message ClimateCommandRequest {
|
|||
float target_temperature_low = 7;
|
||||
bool has_target_temperature_high = 8;
|
||||
float target_temperature_high = 9;
|
||||
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
|
||||
bool has_legacy_away = 10;
|
||||
bool legacy_away = 11;
|
||||
bool unused_has_legacy_away = 10;
|
||||
bool unused_legacy_away = 11;
|
||||
bool has_fan_mode = 12;
|
||||
ClimateFanMode fan_mode = 13;
|
||||
bool has_swing_mode = 14;
|
||||
|
|
|
@ -530,7 +530,6 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
|||
resp.custom_fan_mode = climate->custom_fan_mode.value();
|
||||
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
||||
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
||||
resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
|
||||
resp.custom_preset = climate->custom_preset.value();
|
||||
|
@ -591,8 +590,6 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
|||
call.set_target_temperature_low(msg.target_temperature_low);
|
||||
if (msg.has_target_temperature_high)
|
||||
call.set_target_temperature_high(msg.target_temperature_high);
|
||||
if (msg.has_legacy_away)
|
||||
call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
|
||||
if (msg.has_fan_mode)
|
||||
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
|
||||
if (msg.has_custom_fan_mode)
|
||||
|
@ -944,7 +941,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
|||
|
||||
HelloResponse resp;
|
||||
resp.api_version_major = 1;
|
||||
resp.api_version_minor = 7;
|
||||
resp.api_version_minor = 8;
|
||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||
resp.name = App.get_name();
|
||||
|
||||
|
|
|
@ -941,6 +941,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
|
|||
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
this->supports_stop = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -993,6 +997,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
|
|||
buffer.encode_bool(9, this->disabled_by_default);
|
||||
buffer.encode_string(10, this->icon);
|
||||
buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
|
||||
buffer.encode_bool(12, this->supports_stop);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||
|
@ -1042,6 +1047,10 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
|||
out.append(" entity_category: ");
|
||||
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_stop: ");
|
||||
out.append(YESNO(this->supports_stop));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
@ -3649,7 +3658,7 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->legacy_away = value.as_bool();
|
||||
this->unused_legacy_away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
|
@ -3719,7 +3728,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||
buffer.encode_float(4, this->target_temperature);
|
||||
buffer.encode_float(5, this->target_temperature_low);
|
||||
buffer.encode_float(6, this->target_temperature_high);
|
||||
buffer.encode_bool(7, this->legacy_away);
|
||||
buffer.encode_bool(7, this->unused_legacy_away);
|
||||
buffer.encode_enum<enums::ClimateAction>(8, this->action);
|
||||
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
|
||||
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
|
||||
|
@ -3760,8 +3769,8 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
|||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" legacy_away: ");
|
||||
out.append(YESNO(this->legacy_away));
|
||||
out.append(" unused_legacy_away: ");
|
||||
out.append(YESNO(this->unused_legacy_away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" action: ");
|
||||
|
@ -3813,11 +3822,11 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
|||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->has_legacy_away = value.as_bool();
|
||||
this->unused_has_legacy_away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 11: {
|
||||
this->legacy_away = value.as_bool();
|
||||
this->unused_legacy_away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
|
@ -3902,8 +3911,8 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
|||
buffer.encode_float(7, this->target_temperature_low);
|
||||
buffer.encode_bool(8, this->has_target_temperature_high);
|
||||
buffer.encode_float(9, this->target_temperature_high);
|
||||
buffer.encode_bool(10, this->has_legacy_away);
|
||||
buffer.encode_bool(11, this->legacy_away);
|
||||
buffer.encode_bool(10, this->unused_has_legacy_away);
|
||||
buffer.encode_bool(11, this->unused_legacy_away);
|
||||
buffer.encode_bool(12, this->has_fan_mode);
|
||||
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
|
||||
buffer.encode_bool(14, this->has_swing_mode);
|
||||
|
@ -3959,12 +3968,12 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
|||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_legacy_away: ");
|
||||
out.append(YESNO(this->has_legacy_away));
|
||||
out.append(" unused_has_legacy_away: ");
|
||||
out.append(YESNO(this->unused_has_legacy_away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" legacy_away: ");
|
||||
out.append(YESNO(this->legacy_away));
|
||||
out.append(" unused_legacy_away: ");
|
||||
out.append(YESNO(this->unused_legacy_away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_fan_mode: ");
|
||||
|
|
|
@ -375,6 +375,7 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
|||
bool disabled_by_default{false};
|
||||
std::string icon{};
|
||||
enums::EntityCategory entity_category{};
|
||||
bool supports_stop{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
|
@ -958,7 +959,7 @@ class ClimateStateResponse : public ProtoMessage {
|
|||
float target_temperature{0.0f};
|
||||
float target_temperature_low{0.0f};
|
||||
float target_temperature_high{0.0f};
|
||||
bool legacy_away{false};
|
||||
bool unused_legacy_away{false};
|
||||
enums::ClimateAction action{};
|
||||
enums::ClimateFanMode fan_mode{};
|
||||
enums::ClimateSwingMode swing_mode{};
|
||||
|
@ -986,8 +987,8 @@ class ClimateCommandRequest : public ProtoMessage {
|
|||
float target_temperature_low{0.0f};
|
||||
bool has_target_temperature_high{false};
|
||||
float target_temperature_high{0.0f};
|
||||
bool has_legacy_away{false};
|
||||
bool legacy_away{false};
|
||||
bool unused_has_legacy_away{false};
|
||||
bool unused_legacy_away{false};
|
||||
bool has_fan_mode{false};
|
||||
enums::ClimateFanMode fan_mode{};
|
||||
bool has_swing_mode{false};
|
||||
|
|
|
@ -428,14 +428,17 @@ void APIServer::on_shutdown() {
|
|||
}
|
||||
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void APIServer::start_voice_assistant() {
|
||||
bool APIServer::start_voice_assistant() {
|
||||
for (auto &c : this->clients_) {
|
||||
c->request_voice_assistant(true);
|
||||
if (c->request_voice_assistant(true))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void APIServer::stop_voice_assistant() {
|
||||
for (auto &c : this->clients_) {
|
||||
c->request_voice_assistant(false);
|
||||
if (c->request_voice_assistant(false))
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -96,7 +96,7 @@ class APIServer : public Component, public Controller {
|
|||
#endif
|
||||
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
void start_voice_assistant();
|
||||
bool start_voice_assistant();
|
||||
void stop_voice_assistant();
|
||||
#endif
|
||||
|
||||
|
|
|
@ -43,12 +43,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
|
|||
}
|
||||
|
||||
BinarySensor::BinarySensor() : state(false) {}
|
||||
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string BinarySensor::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
return "";
|
||||
}
|
||||
|
||||
void BinarySensor::add_filter(Filter *filter) {
|
||||
filter->parent_ = this;
|
||||
if (this->filter_list_ == nullptr) {
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace binary_sensor {
|
|||
* The sub classes should notify the front-end of new states via the publish_state() method which
|
||||
* handles inverted inputs for you.
|
||||
*/
|
||||
class BinarySensor : public EntityBase {
|
||||
class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
explicit BinarySensor();
|
||||
|
||||
|
@ -60,12 +60,6 @@ class BinarySensor : public EntityBase {
|
|||
/// The current reported state of the binary sensor.
|
||||
bool state;
|
||||
|
||||
/// Manually set the Home Assistant device class (see binary_sensor::device_class)
|
||||
void set_device_class(const std::string &device_class);
|
||||
|
||||
/// Get the device class for this binary sensor, using the manual override if specified.
|
||||
std::string get_device_class();
|
||||
|
||||
void add_filter(Filter *filter);
|
||||
void add_filters(const std::vector<Filter *> &filters);
|
||||
|
||||
|
@ -82,7 +76,6 @@ class BinarySensor : public EntityBase {
|
|||
|
||||
protected:
|
||||
CallbackManager<void(bool)> state_callback_{};
|
||||
optional<std::string> device_class_{}; ///< Stores the override of the device class
|
||||
Filter *filter_list_{nullptr};
|
||||
bool has_state_{false};
|
||||
bool publish_initial_state_{false};
|
||||
|
|
|
@ -13,8 +13,5 @@ void Button::press() {
|
|||
}
|
||||
void Button::add_on_press_callback(std::function<void()> &&callback) { this->press_callback_.add(std::move(callback)); }
|
||||
|
||||
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string Button::get_device_class() { return this->device_class_; }
|
||||
|
||||
} // namespace button
|
||||
} // namespace esphome
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace button {
|
|||
*
|
||||
* A button is just a momentary switch that does not have a state, only a trigger.
|
||||
*/
|
||||
class Button : public EntityBase {
|
||||
class Button : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
/** Press this button. This is called by the front-end.
|
||||
*
|
||||
|
@ -40,19 +40,12 @@ class Button : public EntityBase {
|
|||
*/
|
||||
void add_on_press_callback(std::function<void()> &&callback);
|
||||
|
||||
/// Set the Home Assistant device class (see button::device_class).
|
||||
void set_device_class(const std::string &device_class);
|
||||
|
||||
/// Get the device class for this button.
|
||||
std::string get_device_class();
|
||||
|
||||
protected:
|
||||
/** You should implement this virtual method if you want to create your own button.
|
||||
*/
|
||||
virtual void press_action() = 0;
|
||||
|
||||
CallbackManager<void()> press_callback_{};
|
||||
std::string device_class_{};
|
||||
};
|
||||
|
||||
} // namespace button
|
||||
|
|
|
@ -343,7 +343,7 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
|
|||
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
|
||||
cv.Optional(CONF_AWAY): cv.invalid("Use preset instead"),
|
||||
cv.Exclusive(CONF_FAN_MODE, "fan_mode"): cv.templatable(
|
||||
validate_climate_fan_mode
|
||||
),
|
||||
|
@ -379,9 +379,6 @@ async def climate_control_to_code(config, action_id, template_arg, args):
|
|||
config[CONF_TARGET_TEMPERATURE_HIGH], args, float
|
||||
)
|
||||
cg.add(var.set_target_temperature_high(template_))
|
||||
if CONF_AWAY in config:
|
||||
template_ = await cg.templatable(config[CONF_AWAY], args, bool)
|
||||
cg.add(var.set_away(template_))
|
||||
if CONF_FAN_MODE in config:
|
||||
template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
|
||||
cg.add(var.set_fan_mode(template_))
|
||||
|
|
|
@ -264,25 +264,11 @@ const optional<ClimateMode> &ClimateCall::get_mode() const { return this->mode_;
|
|||
const optional<float> &ClimateCall::get_target_temperature() const { return this->target_temperature_; }
|
||||
const optional<float> &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; }
|
||||
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
|
||||
optional<bool> ClimateCall::get_away() const {
|
||||
if (!this->preset_.has_value())
|
||||
return {};
|
||||
return *this->preset_ == ClimatePreset::CLIMATE_PRESET_AWAY;
|
||||
}
|
||||
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
|
||||
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
|
||||
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
|
||||
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
|
||||
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
|
||||
ClimateCall &ClimateCall::set_away(bool away) {
|
||||
this->preset_ = away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_away(optional<bool> away) {
|
||||
if (away.has_value())
|
||||
this->preset_ = *away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_target_temperature_high(optional<float> target_temperature_high) {
|
||||
this->target_temperature_high_ = target_temperature_high;
|
||||
return *this;
|
||||
|
|
|
@ -64,10 +64,6 @@ class ClimateCall {
|
|||
* For climate devices with two point target temperature control
|
||||
*/
|
||||
ClimateCall &set_target_temperature_high(optional<float> target_temperature_high);
|
||||
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
|
||||
ClimateCall &set_away(bool away);
|
||||
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
|
||||
ClimateCall &set_away(optional<bool> away);
|
||||
/// Set the fan mode of the climate device.
|
||||
ClimateCall &set_fan_mode(ClimateFanMode fan_mode);
|
||||
/// Set the fan mode of the climate device.
|
||||
|
@ -97,8 +93,6 @@ class ClimateCall {
|
|||
const optional<float> &get_target_temperature() const;
|
||||
const optional<float> &get_target_temperature_low() const;
|
||||
const optional<float> &get_target_temperature_high() const;
|
||||
ESPDEPRECATED("get_away() is deprecated, please use .get_preset() instead", "v1.20")
|
||||
optional<bool> get_away() const;
|
||||
const optional<ClimateFanMode> &get_fan_mode() const;
|
||||
const optional<ClimateSwingMode> &get_swing_mode() const;
|
||||
const optional<std::string> &get_custom_fan_mode() const;
|
||||
|
@ -184,14 +178,6 @@ class Climate : public EntityBase {
|
|||
};
|
||||
};
|
||||
|
||||
/** Whether the climate device is in away mode.
|
||||
*
|
||||
* Away allows climate devices to have two different target temperature configs:
|
||||
* one for normal mode and one for away mode.
|
||||
*/
|
||||
ESPDEPRECATED("away is deprecated, use preset instead", "v1.20")
|
||||
bool away{false};
|
||||
|
||||
/// The active fan mode of the climate device.
|
||||
optional<ClimateFanMode> fan_mode;
|
||||
|
||||
|
|
|
@ -117,15 +117,6 @@ class ClimateTraits {
|
|||
bool supports_custom_preset(const std::string &custom_preset) const {
|
||||
return supported_custom_presets_.count(custom_preset);
|
||||
}
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_presets() instead", "v1.20")
|
||||
void set_supports_away(bool supports) {
|
||||
if (supports) {
|
||||
supported_presets_.insert(CLIMATE_PRESET_AWAY);
|
||||
supported_presets_.insert(CLIMATE_PRESET_HOME);
|
||||
}
|
||||
}
|
||||
ESPDEPRECATED("This method is deprecated, use supports_preset() instead", "v1.20")
|
||||
bool get_supports_away() const { return supports_preset(CLIMATE_PRESET_AWAY); }
|
||||
|
||||
void set_supported_swing_modes(std::set<ClimateSwingMode> modes) { supported_swing_modes_ = std::move(modes); }
|
||||
void add_supported_swing_mode(ClimateSwingMode mode) { supported_swing_modes_.insert(mode); }
|
||||
|
|
|
@ -28,6 +28,7 @@ cover::CoverTraits CopyCover::get_traits() {
|
|||
// copy traits manually so it doesn't break when new options are added
|
||||
// but the control() method hasn't implemented them yet.
|
||||
traits.set_is_assumed_state(base.get_is_assumed_state());
|
||||
traits.set_supports_stop(base.get_supports_stop());
|
||||
traits.set_supports_position(base.get_supports_position());
|
||||
traits.set_supports_tilt(base.get_supports_tilt());
|
||||
traits.set_supports_toggle(base.get_supports_toggle());
|
||||
|
|
|
@ -145,7 +145,7 @@ CoverCall &CoverCall::set_stop(bool stop) {
|
|||
return *this;
|
||||
}
|
||||
bool CoverCall::get_stop() const { return this->stop_; }
|
||||
void Cover::set_device_class(const std::string &device_class) { this->device_class_override_ = device_class; }
|
||||
|
||||
CoverCall Cover::make_call() { return {this}; }
|
||||
void Cover::open() {
|
||||
auto call = this->make_call();
|
||||
|
@ -204,11 +204,7 @@ optional<CoverRestoreState> Cover::restore_state_() {
|
|||
return {};
|
||||
return recovered;
|
||||
}
|
||||
std::string Cover::get_device_class() {
|
||||
if (this->device_class_override_.has_value())
|
||||
return *this->device_class_override_;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool Cover::is_fully_open() const { return this->position == COVER_OPEN; }
|
||||
bool Cover::is_fully_closed() const { return this->position == COVER_CLOSED; }
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ const char *cover_operation_to_str(CoverOperation op);
|
|||
* to control all values of the cover. Also implement get_traits() to return what operations
|
||||
* the cover supports.
|
||||
*/
|
||||
class Cover : public EntityBase {
|
||||
class Cover : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
explicit Cover();
|
||||
|
||||
|
@ -156,8 +156,6 @@ class Cover : public EntityBase {
|
|||
void publish_state(bool save = true);
|
||||
|
||||
virtual CoverTraits get_traits() = 0;
|
||||
void set_device_class(const std::string &device_class);
|
||||
std::string get_device_class();
|
||||
|
||||
/// Helper method to check if the cover is fully open. Equivalent to comparing .position against 1.0
|
||||
bool is_fully_open() const;
|
||||
|
@ -172,7 +170,6 @@ class Cover : public EntityBase {
|
|||
optional<CoverRestoreState> restore_state_();
|
||||
|
||||
CallbackManager<void()> state_callback_{};
|
||||
optional<std::string> device_class_override_{};
|
||||
|
||||
ESPPreferenceObject rtc_;
|
||||
};
|
||||
|
|
|
@ -15,12 +15,15 @@ class CoverTraits {
|
|||
void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; }
|
||||
bool get_supports_toggle() const { return this->supports_toggle_; }
|
||||
void set_supports_toggle(bool supports_toggle) { this->supports_toggle_ = supports_toggle; }
|
||||
bool get_supports_stop() const { return this->supports_stop_; }
|
||||
void set_supports_stop(bool supports_stop) { this->supports_stop_ = supports_stop; }
|
||||
|
||||
protected:
|
||||
bool is_assumed_state_{false};
|
||||
bool supports_position_{false};
|
||||
bool supports_tilt_{false};
|
||||
bool supports_toggle_{false};
|
||||
bool supports_stop_{false};
|
||||
};
|
||||
|
||||
} // namespace cover
|
||||
|
|
|
@ -12,6 +12,7 @@ using namespace esphome::cover;
|
|||
|
||||
CoverTraits CurrentBasedCover::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_toggle(true);
|
||||
traits.set_is_assumed_state(false);
|
||||
|
|
|
@ -17,26 +17,29 @@ debug_ns = cg.esphome_ns.namespace("debug")
|
|||
DebugComponent = debug_ns.class_("DebugComponent", cg.PollingComponent)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DebugComponent),
|
||||
cv.Optional(CONF_DEVICE): cv.invalid(
|
||||
"The 'device' option has been moved to the 'debug' text_sensor component"
|
||||
),
|
||||
cv.Optional(CONF_FREE): cv.invalid(
|
||||
"The 'free' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
cv.Optional(CONF_BLOCK): cv.invalid(
|
||||
"The 'block' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
cv.Optional(CONF_FRAGMENTATION): cv.invalid(
|
||||
"The 'fragmentation' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
cv.Optional(CONF_LOOP_TIME): cv.invalid(
|
||||
"The 'loop_time' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DebugComponent),
|
||||
cv.Optional(CONF_DEVICE): cv.invalid(
|
||||
"The 'device' option has been moved to the 'debug' text_sensor component"
|
||||
),
|
||||
cv.Optional(CONF_FREE): cv.invalid(
|
||||
"The 'free' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
cv.Optional(CONF_BLOCK): cv.invalid(
|
||||
"The 'block' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
cv.Optional(CONF_FRAGMENTATION): cv.invalid(
|
||||
"The 'fragmentation' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
cv.Optional(CONF_LOOP_TIME): cv.invalid(
|
||||
"The 'loop_time' option has been moved to the 'debug' sensor component"
|
||||
),
|
||||
}
|
||||
).extend(cv.polling_component_schema("60s")),
|
||||
cv.only_on(["esp32", "esp8266"]),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
|
|
@ -72,6 +72,7 @@ class DemoCover : public cover::Cover, public Component {
|
|||
traits.set_supports_tilt(true);
|
||||
break;
|
||||
case DemoCoverType::TYPE_4:
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_is_assumed_state(true);
|
||||
traits.set_supports_tilt(true);
|
||||
break;
|
||||
|
|
|
@ -11,6 +11,7 @@ using namespace esphome::cover;
|
|||
|
||||
CoverTraits EndstopCover::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_toggle(true);
|
||||
traits.set_is_assumed_state(false);
|
||||
|
|
|
@ -163,7 +163,7 @@ RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5)
|
|||
# The platformio/espressif32 version to use for arduino frameworks
|
||||
# - https://github.com/platformio/platform-espressif32/releases
|
||||
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
|
||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 2, 0)
|
||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 3, 0)
|
||||
|
||||
# The default/recommended esp-idf framework version
|
||||
# - https://github.com/espressif/esp-idf/releases
|
||||
|
@ -252,7 +252,7 @@ def _parse_platform_version(value):
|
|||
try:
|
||||
# if platform version is a valid version constraint, prefix the default package
|
||||
cv.platformio_version_constraint(value)
|
||||
return f"platformio/espressif32 @ {value}"
|
||||
return f"platformio/espressif32@{value}"
|
||||
except cv.Invalid:
|
||||
return value
|
||||
|
||||
|
@ -367,12 +367,12 @@ async def to_code(config):
|
|||
cg.add_build_flag("-Wno-nonnull-compare")
|
||||
cg.add_platformio_option(
|
||||
"platform_packages",
|
||||
[f"platformio/framework-espidf @ {conf[CONF_SOURCE]}"],
|
||||
[f"platformio/framework-espidf@{conf[CONF_SOURCE]}"],
|
||||
)
|
||||
# platformio/toolchain-esp32ulp does not support linux_aarch64 yet and has not been updated for over 2 years
|
||||
# This is espressif's own published version which is more up to date.
|
||||
cg.add_platformio_option(
|
||||
"platform_packages", ["espressif/toolchain-esp32ulp @ 2.35.0-20220830"]
|
||||
"platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"]
|
||||
)
|
||||
add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False)
|
||||
add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True)
|
||||
|
@ -433,7 +433,7 @@ async def to_code(config):
|
|||
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
|
||||
cg.add_platformio_option(
|
||||
"platform_packages",
|
||||
[f"platformio/framework-arduinoespressif32 @ {conf[CONF_SOURCE]}"],
|
||||
[f"platformio/framework-arduinoespressif32@{conf[CONF_SOURCE]}"],
|
||||
)
|
||||
|
||||
cg.add_platformio_option("board_build.partitions", "partitions.csv")
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <freertos/task.h>
|
||||
#include <esp_idf_version.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include <esp_timer.h>
|
||||
#include <soc/rtc.h>
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
|
|
|
@ -10,7 +10,7 @@ CONFLICTS_WITH = ["esp32_ble_beacon"]
|
|||
|
||||
CONF_BLE_ID = "ble_id"
|
||||
|
||||
NO_BLUTOOTH_VARIANTS = [const.VARIANT_ESP32S2]
|
||||
NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
|
||||
|
||||
esp32_ble_ns = cg.esphome_ns.namespace("esp32_ble")
|
||||
ESP32BLE = esp32_ble_ns.class_("ESP32BLE", cg.Component)
|
||||
|
@ -29,7 +29,7 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
|
||||
def validate_variant(_):
|
||||
variant = get_esp32_variant()
|
||||
if variant in NO_BLUTOOTH_VARIANTS:
|
||||
if variant in NO_BLUETOOTH_VARIANTS:
|
||||
raise cv.Invalid(f"{variant} does not support Bluetooth")
|
||||
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ def _parse_platform_version(value):
|
|||
try:
|
||||
# if platform version is a valid version constraint, prefix the default package
|
||||
cv.platformio_version_constraint(value)
|
||||
return f"platformio/espressif8266 @ {value}"
|
||||
return f"platformio/espressif8266@{value}"
|
||||
except cv.Invalid:
|
||||
return value
|
||||
|
||||
|
@ -181,7 +181,7 @@ async def to_code(config):
|
|||
cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
|
||||
cg.add_platformio_option(
|
||||
"platform_packages",
|
||||
[f"platformio/framework-arduinoespressif8266 @ {conf[CONF_SOURCE]}"],
|
||||
[f"platformio/framework-arduinoespressif8266@{conf[CONF_SOURCE]}"],
|
||||
)
|
||||
|
||||
# Default for platformio is LWIP2_LOW_MEMORY with:
|
||||
|
|
|
@ -26,8 +26,10 @@ EthernetComponent::EthernetComponent() { global_eth_component = this; }
|
|||
|
||||
void EthernetComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Ethernet...");
|
||||
// Delay here to allow power to stabilise before Ethernet is initialised.
|
||||
delay(300); // NOLINT
|
||||
if (esp_reset_reason() != ESP_RST_DEEPSLEEP) {
|
||||
// Delay here to allow power to stabilise before Ethernet is initialized.
|
||||
delay(300); // NOLINT
|
||||
}
|
||||
|
||||
esp_err_t err;
|
||||
err = esp_netif_init();
|
||||
|
@ -52,30 +54,29 @@ void EthernetComponent::setup() {
|
|||
|
||||
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
|
||||
|
||||
esp_eth_phy_t *phy;
|
||||
switch (this->type_) {
|
||||
case ETHERNET_TYPE_LAN8720: {
|
||||
phy = esp_eth_phy_new_lan87xx(&phy_config);
|
||||
this->phy_ = esp_eth_phy_new_lan87xx(&phy_config);
|
||||
break;
|
||||
}
|
||||
case ETHERNET_TYPE_RTL8201: {
|
||||
phy = esp_eth_phy_new_rtl8201(&phy_config);
|
||||
this->phy_ = esp_eth_phy_new_rtl8201(&phy_config);
|
||||
break;
|
||||
}
|
||||
case ETHERNET_TYPE_DP83848: {
|
||||
phy = esp_eth_phy_new_dp83848(&phy_config);
|
||||
this->phy_ = esp_eth_phy_new_dp83848(&phy_config);
|
||||
break;
|
||||
}
|
||||
case ETHERNET_TYPE_IP101: {
|
||||
phy = esp_eth_phy_new_ip101(&phy_config);
|
||||
this->phy_ = esp_eth_phy_new_ip101(&phy_config);
|
||||
break;
|
||||
}
|
||||
case ETHERNET_TYPE_JL1101: {
|
||||
phy = esp_eth_phy_new_jl1101(&phy_config);
|
||||
this->phy_ = esp_eth_phy_new_jl1101(&phy_config);
|
||||
break;
|
||||
}
|
||||
case ETHERNET_TYPE_KSZ8081: {
|
||||
phy = esp_eth_phy_new_ksz8081(&phy_config);
|
||||
this->phy_ = esp_eth_phy_new_ksz8081(&phy_config);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -84,7 +85,7 @@ void EthernetComponent::setup() {
|
|||
}
|
||||
}
|
||||
|
||||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
|
||||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, this->phy_);
|
||||
this->eth_handle_ = nullptr;
|
||||
err = esp_eth_driver_install(ð_config, &this->eth_handle_);
|
||||
ESPHL_ERROR_CHECK(err, "ETH driver install error");
|
||||
|
@ -356,6 +357,21 @@ std::string EthernetComponent::get_use_address() const {
|
|||
|
||||
void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; }
|
||||
|
||||
bool EthernetComponent::powerdown() {
|
||||
ESP_LOGI(TAG, "Powering down ethernet PHY");
|
||||
if (this->phy_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Ethernet PHY not assigned");
|
||||
return false;
|
||||
}
|
||||
this->connected_ = false;
|
||||
this->started_ = false;
|
||||
if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error powering down ethernet PHY");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ethernet
|
||||
} // namespace esphome
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ class EthernetComponent : public Component {
|
|||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
bool can_proceed() override;
|
||||
void on_shutdown() override { powerdown(); }
|
||||
bool is_connected();
|
||||
|
||||
void set_phy_addr(uint8_t phy_addr);
|
||||
|
@ -58,6 +59,7 @@ class EthernetComponent : public Component {
|
|||
network::IPAddress get_ip_address();
|
||||
std::string get_use_address() const;
|
||||
void set_use_address(const std::string &use_address);
|
||||
bool powerdown();
|
||||
|
||||
protected:
|
||||
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||
|
@ -82,6 +84,7 @@ class EthernetComponent : public Component {
|
|||
uint32_t connect_begin_;
|
||||
esp_netif_t *eth_netif_{nullptr};
|
||||
esp_eth_handle_t eth_handle_;
|
||||
esp_eth_phy_t *phy_{nullptr};
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
|
|
@ -41,6 +41,7 @@ void FeedbackCover::setup() {
|
|||
|
||||
CoverTraits FeedbackCover::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_toggle(true);
|
||||
traits.set_is_assumed_state(this->assumed_state_);
|
||||
|
|
|
@ -77,10 +77,12 @@ void FingerprintGrowComponent::finish_enrollment(uint8_t result) {
|
|||
this->enrollment_done_callback_.call(this->enrollment_slot_);
|
||||
this->get_fingerprint_count_();
|
||||
} else {
|
||||
this->enrollment_failed_callback_.call(this->enrollment_slot_);
|
||||
if (this->enrollment_slot_ != ENROLLMENT_SLOT_UNUSED) {
|
||||
this->enrollment_failed_callback_.call(this->enrollment_slot_);
|
||||
}
|
||||
}
|
||||
this->enrollment_image_ = 0;
|
||||
this->enrollment_slot_ = 0;
|
||||
this->enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
|
||||
if (this->enrolling_binary_sensor_ != nullptr) {
|
||||
this->enrolling_binary_sensor_->publish_state(false);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace fingerprint_grow {
|
|||
|
||||
static const uint16_t START_CODE = 0xEF01;
|
||||
|
||||
static const uint16_t ENROLLMENT_SLOT_UNUSED = 0xFFFF;
|
||||
|
||||
enum GrowPacketType {
|
||||
COMMAND = 0x01,
|
||||
DATA = 0x02,
|
||||
|
@ -158,7 +160,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
|||
uint32_t new_password_ = -1;
|
||||
GPIOPin *sensing_pin_{nullptr};
|
||||
uint8_t enrollment_image_ = 0;
|
||||
uint16_t enrollment_slot_ = 0;
|
||||
uint16_t enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
|
||||
uint8_t enrollment_buffers_ = 5;
|
||||
bool waiting_removal_ = false;
|
||||
uint32_t last_aura_led_control_ = 0;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "i2c_bus_arduino.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include <Arduino.h>
|
||||
#include <cstring>
|
||||
|
||||
|
@ -227,10 +228,14 @@ void ArduinoI2CBus::recover_() {
|
|||
// When SCL is kept LOW at this point, we might be looking at a device
|
||||
// that applies clock stretching. Wait for the release of the SCL line,
|
||||
// but not forever. There is no specification for the maximum allowed
|
||||
// time. We'll stick to 500ms here.
|
||||
auto wait = 20;
|
||||
// time. We yield and reset the WDT, so as to avoid triggering reset.
|
||||
// No point in trying to recover the bus by forcing a uC reset. Bus
|
||||
// should recover in a few ms or less else not likely to recovery at
|
||||
// all.
|
||||
auto wait = 250;
|
||||
while (wait-- && digitalRead(scl_pin_) == LOW) { // NOLINT
|
||||
delay(25);
|
||||
App.feed_wdt();
|
||||
delayMicroseconds(half_period_usec * 2);
|
||||
}
|
||||
if (digitalRead(scl_pin_) == LOW) { // NOLINT
|
||||
ESP_LOGE(TAG, "Recovery failed: SCL is held LOW during clock pulse cycle");
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include <cstring>
|
||||
#include <cinttypes>
|
||||
|
||||
|
@ -273,10 +274,14 @@ void IDFI2CBus::recover_() {
|
|||
// When SCL is kept LOW at this point, we might be looking at a device
|
||||
// that applies clock stretching. Wait for the release of the SCL line,
|
||||
// but not forever. There is no specification for the maximum allowed
|
||||
// time. We'll stick to 500ms here.
|
||||
auto wait = 20;
|
||||
// time. We yield and reset the WDT, so as to avoid triggering reset.
|
||||
// No point in trying to recover the bus by forcing a uC reset. Bus
|
||||
// should recover in a few ms or less else not likely to recovery at
|
||||
// all.
|
||||
auto wait = 250;
|
||||
while (wait-- && gpio_get_level(scl_pin) == 0) {
|
||||
delay(25);
|
||||
App.feed_wdt();
|
||||
delayMicroseconds(half_period_usec * 2);
|
||||
}
|
||||
if (gpio_get_level(scl_pin) == 0) {
|
||||
ESP_LOGE(TAG, "Recovery failed: SCL is held LOW during clock pulse cycle");
|
||||
|
|
|
@ -93,7 +93,7 @@ async def to_code(config):
|
|||
cg.add(var.set_scroll(config[CONF_SCROLL_ENABLE]))
|
||||
cg.add(var.set_scroll_mode(config[CONF_SCROLL_MODE]))
|
||||
cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE]))
|
||||
cg.add(var.set_flip_x([CONF_FLIP_X]))
|
||||
cg.add(var.set_flip_x(config[CONF_FLIP_X]))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
|
|
|
@ -4,10 +4,13 @@ from esphome.const import (
|
|||
CONF_PROTOCOL,
|
||||
CONF_SERVICES,
|
||||
CONF_SERVICE,
|
||||
KEY_CORE,
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
)
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.components.esp32 import add_idf_component
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
DEPENDENCIES = ["network"]
|
||||
|
@ -79,6 +82,16 @@ async def to_code(config):
|
|||
elif CORE.is_rp2040:
|
||||
cg.add_library("LEAmDNS", None)
|
||||
|
||||
if CORE.using_esp_idf and CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(
|
||||
5, 0, 0
|
||||
):
|
||||
add_idf_component(
|
||||
"mdns",
|
||||
"https://github.com/espressif/esp-protocols.git",
|
||||
"mdns-v1.0.9",
|
||||
"components/mdns",
|
||||
)
|
||||
|
||||
if config[CONF_DISABLED]:
|
||||
return
|
||||
|
||||
|
|
0
esphome/components/mlx90614/__init__.py
Normal file
0
esphome/components/mlx90614/__init__.py
Normal file
122
esphome/components/mlx90614/mlx90614.cpp
Normal file
122
esphome/components/mlx90614/mlx90614.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include "mlx90614.h"
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mlx90614 {
|
||||
|
||||
static const uint8_t MLX90614_RAW_IR_1 = 0x04;
|
||||
static const uint8_t MLX90614_RAW_IR_2 = 0x05;
|
||||
static const uint8_t MLX90614_TEMPERATURE_AMBIENT = 0x06;
|
||||
static const uint8_t MLX90614_TEMPERATURE_OBJECT_1 = 0x07;
|
||||
static const uint8_t MLX90614_TEMPERATURE_OBJECT_2 = 0x08;
|
||||
|
||||
static const uint8_t MLX90614_TOMAX = 0x20;
|
||||
static const uint8_t MLX90614_TOMIN = 0x21;
|
||||
static const uint8_t MLX90614_PWMCTRL = 0x22;
|
||||
static const uint8_t MLX90614_TARANGE = 0x23;
|
||||
static const uint8_t MLX90614_EMISSIVITY = 0x24;
|
||||
static const uint8_t MLX90614_CONFIG = 0x25;
|
||||
static const uint8_t MLX90614_ADDR = 0x2E;
|
||||
static const uint8_t MLX90614_ID1 = 0x3C;
|
||||
static const uint8_t MLX90614_ID2 = 0x3D;
|
||||
static const uint8_t MLX90614_ID3 = 0x3E;
|
||||
static const uint8_t MLX90614_ID4 = 0x3F;
|
||||
|
||||
static const char *const TAG = "mlx90614";
|
||||
|
||||
void MLX90614Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up MLX90614...");
|
||||
if (!this->write_emissivity_()) {
|
||||
ESP_LOGE(TAG, "Communication with MLX90614 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool MLX90614Component::write_emissivity_() {
|
||||
if (std::isnan(this->emissivity_))
|
||||
return true;
|
||||
uint16_t value = (uint16_t) (this->emissivity_ * 65535);
|
||||
if (!this->write_bytes_(MLX90614_EMISSIVITY, 0)) {
|
||||
return false;
|
||||
}
|
||||
delay(10);
|
||||
if (!this->write_bytes_(MLX90614_EMISSIVITY, value)) {
|
||||
return false;
|
||||
}
|
||||
delay(10);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t MLX90614Component::crc8_pec_(const uint8_t *data, uint8_t len) {
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
uint8_t in = data[i];
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
bool carry = (crc ^ in) & 0x80;
|
||||
crc <<= 1;
|
||||
if (carry)
|
||||
crc ^= 0x07;
|
||||
in <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) {
|
||||
uint8_t buf[5];
|
||||
buf[0] = this->address_ << 1;
|
||||
buf[1] = reg;
|
||||
buf[2] = data & 0xFF;
|
||||
buf[3] = data >> 8;
|
||||
buf[4] = this->crc8_pec_(buf, 4);
|
||||
return this->write_bytes(reg, buf + 2, 3);
|
||||
}
|
||||
|
||||
void MLX90614Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MLX90614:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with MLX90614 failed!");
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Ambient", this->ambient_sensor_);
|
||||
LOG_SENSOR(" ", "Object", this->object_sensor_);
|
||||
}
|
||||
|
||||
float MLX90614Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void MLX90614Component::update() {
|
||||
uint8_t emissivity[3];
|
||||
if (this->read_register(MLX90614_EMISSIVITY, emissivity, 3, false) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
uint8_t raw_object[3];
|
||||
if (this->read_register(MLX90614_TEMPERATURE_OBJECT_1, raw_object, 3, false) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t raw_ambient[3];
|
||||
if (this->read_register(MLX90614_TEMPERATURE_AMBIENT, raw_ambient, 3, false) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
float ambient = raw_ambient[1] & 0x80 ? NAN : encode_uint16(raw_ambient[1], raw_ambient[0]) * 0.02f - 273.15f;
|
||||
float object = raw_object[1] & 0x80 ? NAN : encode_uint16(raw_object[1], raw_object[0]) * 0.02f - 273.15f;
|
||||
|
||||
ESP_LOGD(TAG, "Got Temperature=%.1f°C Ambient=%.1f°C", object, ambient);
|
||||
|
||||
if (this->ambient_sensor_ != nullptr && !std::isnan(ambient))
|
||||
this->ambient_sensor_->publish_state(ambient);
|
||||
if (this->object_sensor_ != nullptr && !std::isnan(object))
|
||||
this->object_sensor_->publish_state(object);
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
} // namespace mlx90614
|
||||
} // namespace esphome
|
34
esphome/components/mlx90614/mlx90614.h
Normal file
34
esphome/components/mlx90614/mlx90614.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mlx90614 {
|
||||
|
||||
class MLX90614Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
float get_setup_priority() const override;
|
||||
|
||||
void set_ambient_sensor(sensor::Sensor *ambient_sensor) { ambient_sensor_ = ambient_sensor; }
|
||||
void set_object_sensor(sensor::Sensor *object_sensor) { object_sensor_ = object_sensor; }
|
||||
|
||||
void set_emissivity(float emissivity) { emissivity_ = emissivity; }
|
||||
|
||||
protected:
|
||||
bool write_emissivity_();
|
||||
|
||||
uint8_t crc8_pec_(const uint8_t *data, uint8_t len);
|
||||
bool write_bytes_(uint8_t reg, uint16_t data);
|
||||
|
||||
sensor::Sensor *ambient_sensor_{nullptr};
|
||||
sensor::Sensor *object_sensor_{nullptr};
|
||||
|
||||
float emissivity_{NAN};
|
||||
};
|
||||
} // namespace mlx90614
|
||||
} // namespace esphome
|
63
esphome/components/mlx90614/sensor.py
Normal file
63
esphome/components/mlx90614/sensor.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_AMBIENT = "ambient"
|
||||
CONF_EMISSIVITY = "emissivity"
|
||||
CONF_OBJECT = "object"
|
||||
|
||||
mlx90614_ns = cg.esphome_ns.namespace("mlx90614")
|
||||
MLX90614Component = mlx90614_ns.class_(
|
||||
"MLX90614Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(MLX90614Component),
|
||||
cv.Optional(CONF_AMBIENT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_OBJECT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_EMISSIVITY, default=1.0): cv.percentage,
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x5A))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_AMBIENT in config:
|
||||
sens = await sensor.new_sensor(config[CONF_AMBIENT])
|
||||
cg.add(var.set_ambient_sensor(sens))
|
||||
|
||||
if CONF_OBJECT in config:
|
||||
sens = await sensor.new_sensor(config[CONF_OBJECT])
|
||||
cg.add(var.set_object_sensor(sens))
|
||||
|
||||
cg.add(var.set_emissivity(config[CONF_OBJECT][CONF_EMISSIVITY]))
|
|
@ -75,13 +75,8 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
|
|||
JsonArray presets = root.createNestedArray("preset_modes");
|
||||
if (traits.supports_preset(CLIMATE_PRESET_HOME))
|
||||
presets.add("home");
|
||||
if (traits.supports_preset(CLIMATE_PRESET_AWAY)) {
|
||||
// away_mode_command_topic
|
||||
root[MQTT_AWAY_MODE_COMMAND_TOPIC] = this->get_away_command_topic();
|
||||
// away_mode_state_topic
|
||||
root[MQTT_AWAY_MODE_STATE_TOPIC] = this->get_away_state_topic();
|
||||
if (traits.supports_preset(CLIMATE_PRESET_AWAY))
|
||||
presets.add("away");
|
||||
}
|
||||
if (traits.supports_preset(CLIMATE_PRESET_BOOST))
|
||||
presets.add("boost");
|
||||
if (traits.supports_preset(CLIMATE_PRESET_COMFORT))
|
||||
|
@ -197,29 +192,6 @@ void MQTTClimateComponent::setup() {
|
|||
});
|
||||
}
|
||||
|
||||
if (traits.supports_preset(CLIMATE_PRESET_AWAY)) {
|
||||
this->subscribe(this->get_away_command_topic(), [this](const std::string &topic, const std::string &payload) {
|
||||
auto onoff = parse_on_off(payload.c_str());
|
||||
auto call = this->device_->make_call();
|
||||
switch (onoff) {
|
||||
case PARSE_ON:
|
||||
call.set_preset(CLIMATE_PRESET_AWAY);
|
||||
break;
|
||||
case PARSE_OFF:
|
||||
call.set_preset(CLIMATE_PRESET_HOME);
|
||||
break;
|
||||
case PARSE_TOGGLE:
|
||||
call.set_preset(this->device_->preset == CLIMATE_PRESET_AWAY ? CLIMATE_PRESET_HOME : CLIMATE_PRESET_AWAY);
|
||||
break;
|
||||
case PARSE_NONE:
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unknown payload '%s'", payload.c_str());
|
||||
return;
|
||||
}
|
||||
call.perform();
|
||||
});
|
||||
}
|
||||
|
||||
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
|
||||
this->subscribe(this->get_preset_command_topic(), [this](const std::string &topic, const std::string &payload) {
|
||||
auto call = this->device_->make_call();
|
||||
|
@ -301,11 +273,6 @@ bool MQTTClimateComponent::publish_state_() {
|
|||
success = false;
|
||||
}
|
||||
|
||||
if (traits.supports_preset(CLIMATE_PRESET_AWAY)) {
|
||||
std::string payload = ONOFF(this->device_->preset == CLIMATE_PRESET_AWAY);
|
||||
if (!this->publish(this->get_away_state_topic(), payload))
|
||||
success = false;
|
||||
}
|
||||
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
|
||||
std::string payload;
|
||||
if (this->device_->preset.has_value()) {
|
||||
|
|
|
@ -16,13 +16,5 @@ std::string NumberTraits::get_unit_of_measurement() {
|
|||
return "";
|
||||
}
|
||||
|
||||
void NumberTraits::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
|
||||
std::string NumberTraits::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace number
|
||||
} // namespace esphome
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
|
@ -11,7 +12,7 @@ enum NumberMode : uint8_t {
|
|||
NUMBER_MODE_SLIDER = 2,
|
||||
};
|
||||
|
||||
class NumberTraits {
|
||||
class NumberTraits : public EntityBase_DeviceClass {
|
||||
public:
|
||||
// Set/get the number value boundaries.
|
||||
void set_min_value(float min_value) { min_value_ = min_value; }
|
||||
|
@ -32,17 +33,12 @@ class NumberTraits {
|
|||
void set_mode(NumberMode mode) { this->mode_ = mode; }
|
||||
NumberMode get_mode() const { return this->mode_; }
|
||||
|
||||
// Set/get the device class.
|
||||
void set_device_class(const std::string &device_class);
|
||||
std::string get_device_class();
|
||||
|
||||
protected:
|
||||
float min_value_ = NAN;
|
||||
float max_value_ = NAN;
|
||||
float step_ = NAN;
|
||||
optional<std::string> unit_of_measurement_; ///< Unit of measurement override
|
||||
NumberMode mode_{NUMBER_MODE_AUTO};
|
||||
optional<std::string> device_class_;
|
||||
};
|
||||
|
||||
} // namespace number
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
#include <esp_ota_ops.h>
|
||||
#include "esphome/components/md5/md5.h"
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
#include <spi_flash_mmap.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace ota {
|
||||
|
||||
|
@ -16,9 +20,28 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
|
|||
if (this->partition_ == nullptr) {
|
||||
return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION;
|
||||
}
|
||||
esp_task_wdt_init(15, false); // The following function takes longer than the 5 seconds timeout of WDT
|
||||
|
||||
// The following function takes longer than the 5 seconds timeout of WDT
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
esp_task_wdt_config_t wdtc;
|
||||
wdtc.timeout_ms = 15000;
|
||||
wdtc.idle_core_mask = 0;
|
||||
wdtc.trigger_panic = false;
|
||||
esp_task_wdt_reconfigure(&wdtc);
|
||||
#else
|
||||
esp_task_wdt_init(15, false);
|
||||
#endif
|
||||
|
||||
esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_);
|
||||
esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false); // Set the WDT back to the configured timeout
|
||||
|
||||
// Set the WDT back to the configured timeout
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S;
|
||||
esp_task_wdt_reconfigure(&wdtc);
|
||||
#else
|
||||
esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false);
|
||||
#endif
|
||||
|
||||
if (err != ESP_OK) {
|
||||
esp_ota_abort(this->update_handle_);
|
||||
this->update_handle_ = 0;
|
||||
|
|
|
@ -2,7 +2,12 @@ import esphome.codegen as cg
|
|||
import esphome.config_validation as cv
|
||||
from esphome import automation, pins
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN
|
||||
from esphome.const import (
|
||||
CONF_ON_TAG,
|
||||
CONF_ON_TAG_REMOVED,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_RESET_PIN,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@glmnet"]
|
||||
AUTO_LOAD = ["binary_sensor"]
|
||||
|
@ -24,6 +29,11 @@ RC522_SCHEMA = cv.Schema(
|
|||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_TAG_REMOVED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
).extend(cv.polling_component_schema("1s"))
|
||||
|
||||
|
@ -37,5 +47,10 @@ async def setup_rc522(var, config):
|
|||
|
||||
for conf in config.get(CONF_ON_TAG, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
cg.add(var.register_trigger(trigger))
|
||||
cg.add(var.register_ontag_trigger(trigger))
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_TAG_REMOVED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||
cg.add(var.register_ontagremoved_trigger(trigger))
|
||||
await automation.build_automation(trigger, [(cg.std_string, "x")], conf)
|
||||
|
|
|
@ -256,7 +256,7 @@ void RC522::loop() {
|
|||
|
||||
this->current_uid_ = rfid_uid;
|
||||
|
||||
for (auto *trigger : this->triggers_)
|
||||
for (auto *trigger : this->triggers_ontag_)
|
||||
trigger->process(rfid_uid);
|
||||
|
||||
if (report) {
|
||||
|
@ -265,6 +265,11 @@ void RC522::loop() {
|
|||
break;
|
||||
}
|
||||
case STATE_DONE: {
|
||||
if (!this->current_uid_.empty()) {
|
||||
ESP_LOGV(TAG, "Tag '%s' removed", format_uid(this->current_uid_).c_str());
|
||||
for (auto *trigger : this->triggers_ontagremoved_)
|
||||
trigger->process(this->current_uid_);
|
||||
}
|
||||
this->current_uid_ = {};
|
||||
state_ = STATE_INIT;
|
||||
break;
|
||||
|
|
|
@ -24,7 +24,8 @@ class RC522 : public PollingComponent {
|
|||
void loop() override;
|
||||
|
||||
void register_tag(RC522BinarySensor *tag) { this->binary_sensors_.push_back(tag); }
|
||||
void register_trigger(RC522Trigger *trig) { this->triggers_.push_back(trig); }
|
||||
void register_ontag_trigger(RC522Trigger *trig) { this->triggers_ontag_.push_back(trig); }
|
||||
void register_ontagremoved_trigger(RC522Trigger *trig) { this->triggers_ontagremoved_.push_back(trig); }
|
||||
|
||||
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
|
||||
|
||||
|
@ -242,7 +243,8 @@ class RC522 : public PollingComponent {
|
|||
uint8_t reset_count_{0};
|
||||
uint32_t reset_timeout_{0};
|
||||
std::vector<RC522BinarySensor *> binary_sensors_;
|
||||
std::vector<RC522Trigger *> triggers_;
|
||||
std::vector<RC522Trigger *> triggers_ontag_;
|
||||
std::vector<RC522Trigger *> triggers_ontagremoved_;
|
||||
std::vector<uint8_t> current_uid_;
|
||||
|
||||
enum RC522Error {
|
||||
|
|
|
@ -102,7 +102,7 @@ def _parse_platform_version(value):
|
|||
try:
|
||||
# if platform version is a valid version constraint, prefix the default package
|
||||
cv.platformio_version_constraint(value)
|
||||
return f"platformio/raspberrypi @ {value}"
|
||||
return f"platformio/raspberrypi@{value}"
|
||||
except cv.Invalid:
|
||||
return value
|
||||
|
||||
|
@ -148,7 +148,7 @@ async def to_code(config):
|
|||
cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
|
||||
cg.add_platformio_option(
|
||||
"platform_packages",
|
||||
[f"earlephilhower/framework-arduinopico @ {conf[CONF_SOURCE]}"],
|
||||
[f"earlephilhower/framework-arduinopico@{conf[CONF_SOURCE]}"],
|
||||
)
|
||||
|
||||
cg.add_platformio_option("board_build.core", "earlephilhower")
|
||||
|
|
|
@ -25,10 +25,12 @@ from esphome.const import (
|
|||
CONF_STATE_CLASS,
|
||||
CONF_TO,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
CONF_WINDOW_SIZE,
|
||||
CONF_MQTT_ID,
|
||||
CONF_FORCE_UPDATE,
|
||||
CONF_VALUE,
|
||||
DEVICE_CLASS_APPARENT_POWER,
|
||||
DEVICE_CLASS_AQI,
|
||||
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
||||
|
@ -476,21 +478,38 @@ async def lambda_filter_to_code(config, filter_id):
|
|||
return cg.new_Pvariable(filter_id, lambda_)
|
||||
|
||||
|
||||
DELTA_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_VALUE): cv.positive_float,
|
||||
cv.Optional(CONF_TYPE, default="absolute"): cv.one_of(
|
||||
"absolute", "percentage", lower=True
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def validate_delta(config):
|
||||
try:
|
||||
return (cv.positive_float(config), False)
|
||||
value = cv.positive_float(config)
|
||||
return DELTA_SCHEMA({CONF_VALUE: value, CONF_TYPE: "absolute"})
|
||||
except cv.Invalid:
|
||||
pass
|
||||
try:
|
||||
return (cv.percentage(config), True)
|
||||
value = cv.percentage(config)
|
||||
return DELTA_SCHEMA({CONF_VALUE: value, CONF_TYPE: "percentage"})
|
||||
except cv.Invalid:
|
||||
pass
|
||||
raise cv.Invalid("Delta filter requires a positive number or percentage value.")
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("delta", DeltaFilter, validate_delta)
|
||||
@FILTER_REGISTRY.register("delta", DeltaFilter, cv.Any(DELTA_SCHEMA, validate_delta))
|
||||
async def delta_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(filter_id, *config)
|
||||
percentage = config[CONF_TYPE] == "percentage"
|
||||
return cg.new_Pvariable(
|
||||
filter_id,
|
||||
config[CONF_VALUE],
|
||||
percentage,
|
||||
)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("or", OrFilter, validate_filters)
|
||||
|
|
|
@ -38,13 +38,6 @@ int8_t Sensor::get_accuracy_decimals() {
|
|||
}
|
||||
void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; }
|
||||
|
||||
std::string Sensor::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
return "";
|
||||
}
|
||||
void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
|
||||
void Sensor::set_state_class(StateClass state_class) { this->state_class_ = state_class; }
|
||||
StateClass Sensor::get_state_class() {
|
||||
if (this->state_class_.has_value())
|
||||
|
|
|
@ -54,7 +54,7 @@ std::string state_class_to_string(StateClass state_class);
|
|||
*
|
||||
* A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy.
|
||||
*/
|
||||
class Sensor : public EntityBase {
|
||||
class Sensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
explicit Sensor();
|
||||
|
||||
|
@ -68,11 +68,6 @@ class Sensor : public EntityBase {
|
|||
/// Manually set the accuracy in decimals.
|
||||
void set_accuracy_decimals(int8_t accuracy_decimals);
|
||||
|
||||
/// Get the device class, using the manual override if set.
|
||||
std::string get_device_class();
|
||||
/// Manually set the device class.
|
||||
void set_device_class(const std::string &device_class);
|
||||
|
||||
/// Get the state class, using the manual override if set.
|
||||
StateClass get_state_class();
|
||||
/// Manually set the state class.
|
||||
|
@ -165,7 +160,6 @@ class Sensor : public EntityBase {
|
|||
|
||||
optional<std::string> unit_of_measurement_; ///< Unit of measurement override
|
||||
optional<int8_t> accuracy_decimals_; ///< Accuracy in decimals override
|
||||
optional<std::string> device_class_; ///< Device class override
|
||||
optional<StateClass> state_class_{STATE_CLASS_NONE}; ///< State class override
|
||||
bool force_update_{false}; ///< Force update mode
|
||||
bool has_state_{false};
|
||||
|
|
|
@ -287,18 +287,17 @@ HorizontalCoordinate Sun::calc_coords_() {
|
|||
*/
|
||||
return sun.true_coordinate(m);
|
||||
}
|
||||
optional<time::ESPTime> Sun::calc_event_(bool rising, double zenith) {
|
||||
optional<time::ESPTime> Sun::calc_event_(time::ESPTime date, bool rising, double zenith) {
|
||||
SunAtLocation sun{location_};
|
||||
auto now = this->time_->utcnow();
|
||||
if (!now.is_valid())
|
||||
if (!date.is_valid())
|
||||
return {};
|
||||
// Calculate UT1 timestamp at 0h
|
||||
auto today = now;
|
||||
auto today = date;
|
||||
today.hour = today.minute = today.second = 0;
|
||||
today.recalc_timestamp_utc();
|
||||
|
||||
auto it = sun.event(rising, today, zenith);
|
||||
if (it.has_value() && it->timestamp < now.timestamp) {
|
||||
if (it.has_value() && it->timestamp < date.timestamp) {
|
||||
// We're calculating *next* sunrise/sunset, but calculated event
|
||||
// is today, so try again tomorrow
|
||||
time_t new_timestamp = today.timestamp + 24 * 60 * 60;
|
||||
|
@ -307,9 +306,19 @@ optional<time::ESPTime> Sun::calc_event_(bool rising, double zenith) {
|
|||
}
|
||||
return it;
|
||||
}
|
||||
optional<time::ESPTime> Sun::calc_event_(bool rising, double zenith) {
|
||||
auto it = Sun::calc_event_(this->time_->utcnow(), rising, zenith);
|
||||
return it;
|
||||
}
|
||||
|
||||
optional<time::ESPTime> Sun::sunrise(double elevation) { return this->calc_event_(true, 90 - elevation); }
|
||||
optional<time::ESPTime> Sun::sunset(double elevation) { return this->calc_event_(false, 90 - elevation); }
|
||||
optional<time::ESPTime> Sun::sunrise(time::ESPTime date, double elevation) {
|
||||
return this->calc_event_(date, true, 90 - elevation);
|
||||
}
|
||||
optional<time::ESPTime> Sun::sunset(time::ESPTime date, double elevation) {
|
||||
return this->calc_event_(date, false, 90 - elevation);
|
||||
}
|
||||
double Sun::elevation() { return this->calc_coords_().elevation; }
|
||||
double Sun::azimuth() { return this->calc_coords_().azimuth; }
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ class Sun {
|
|||
|
||||
optional<time::ESPTime> sunrise(double elevation);
|
||||
optional<time::ESPTime> sunset(double elevation);
|
||||
optional<time::ESPTime> sunrise(time::ESPTime date, double elevation);
|
||||
optional<time::ESPTime> sunset(time::ESPTime date, double elevation);
|
||||
|
||||
double elevation();
|
||||
double azimuth();
|
||||
|
@ -66,6 +68,7 @@ class Sun {
|
|||
protected:
|
||||
internal::HorizontalCoordinate calc_coords_();
|
||||
optional<time::ESPTime> calc_event_(bool rising, double zenith);
|
||||
optional<time::ESPTime> calc_event_(time::ESPTime date, bool rising, double zenith);
|
||||
|
||||
time::RealTimeClock *time_;
|
||||
internal::GeoLocation location_;
|
||||
|
|
|
@ -63,13 +63,6 @@ void Switch::add_on_state_callback(std::function<void(bool)> &&callback) {
|
|||
void Switch::set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||
bool Switch::is_inverted() const { return this->inverted_; }
|
||||
|
||||
std::string Switch::get_device_class() {
|
||||
if (this->device_class_.has_value())
|
||||
return *this->device_class_;
|
||||
return "";
|
||||
}
|
||||
void Switch::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
|
||||
void log_switch(const char *tag, const char *prefix, const char *type, Switch *obj) {
|
||||
if (obj != nullptr) {
|
||||
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
|
||||
|
|
|
@ -29,7 +29,7 @@ enum SwitchRestoreMode {
|
|||
* A switch is basically just a combination of a binary sensor (for reporting switch values)
|
||||
* and a write_state method that writes a state to the hardware.
|
||||
*/
|
||||
class Switch : public EntityBase {
|
||||
class Switch : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
explicit Switch();
|
||||
|
||||
|
@ -103,10 +103,6 @@ class Switch : public EntityBase {
|
|||
|
||||
bool is_inverted() const;
|
||||
|
||||
/// Get the device class for this switch.
|
||||
std::string get_device_class();
|
||||
/// Set the Home Assistant device class for this switch.
|
||||
void set_device_class(const std::string &device_class);
|
||||
void set_restore_mode(SwitchRestoreMode restore_mode) { this->restore_mode = restore_mode; }
|
||||
|
||||
protected:
|
||||
|
@ -124,7 +120,6 @@ class Switch : public EntityBase {
|
|||
bool inverted_{false};
|
||||
Deduplicator<bool> publish_dedup_;
|
||||
ESPPreferenceObject rtc_;
|
||||
optional<std::string> device_class_;
|
||||
};
|
||||
|
||||
#define LOG_SWITCH(prefix, type, obj) log_switch((TAG), (prefix), LOG_STR_LITERAL(type), (obj))
|
||||
|
|
|
@ -73,6 +73,7 @@ async def to_code(config):
|
|||
await automation.build_automation(
|
||||
var.get_stop_trigger(), [], config[CONF_STOP_ACTION]
|
||||
)
|
||||
cg.add(var.set_has_stop(True))
|
||||
if CONF_TILT_ACTION in config:
|
||||
await automation.build_automation(
|
||||
var.get_tilt_trigger(), [(float, "tilt")], config[CONF_TILT_ACTION]
|
||||
|
|
|
@ -109,6 +109,7 @@ void TemplateCover::control(const CoverCall &call) {
|
|||
CoverTraits TemplateCover::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_is_assumed_state(this->assumed_state_);
|
||||
traits.set_supports_stop(this->has_stop_);
|
||||
traits.set_supports_position(this->has_position_);
|
||||
traits.set_supports_tilt(this->has_tilt_);
|
||||
return traits;
|
||||
|
@ -116,6 +117,7 @@ CoverTraits TemplateCover::get_traits() {
|
|||
Trigger<float> *TemplateCover::get_position_trigger() const { return this->position_trigger_; }
|
||||
Trigger<float> *TemplateCover::get_tilt_trigger() const { return this->tilt_trigger_; }
|
||||
void TemplateCover::set_tilt_lambda(std::function<optional<float>()> &&tilt_f) { this->tilt_f_ = tilt_f; }
|
||||
void TemplateCover::set_has_stop(bool has_stop) { this->has_stop_ = has_stop; }
|
||||
void TemplateCover::set_has_position(bool has_position) { this->has_position_ = has_position; }
|
||||
void TemplateCover::set_has_tilt(bool has_tilt) { this->has_tilt_ = has_tilt; }
|
||||
void TemplateCover::stop_prev_trigger_() {
|
||||
|
|
|
@ -26,6 +26,7 @@ class TemplateCover : public cover::Cover, public Component {
|
|||
void set_optimistic(bool optimistic);
|
||||
void set_assumed_state(bool assumed_state);
|
||||
void set_tilt_lambda(std::function<optional<float>()> &&tilt_f);
|
||||
void set_has_stop(bool has_stop);
|
||||
void set_has_position(bool has_position);
|
||||
void set_has_tilt(bool has_tilt);
|
||||
void set_restore_mode(TemplateCoverRestoreMode restore_mode) { restore_mode_ = restore_mode; }
|
||||
|
@ -48,6 +49,7 @@ class TemplateCover : public cover::Cover, public Component {
|
|||
bool optimistic_{false};
|
||||
Trigger<> *open_trigger_;
|
||||
Trigger<> *close_trigger_;
|
||||
bool has_stop_{false};
|
||||
Trigger<> *stop_trigger_;
|
||||
Trigger<> *prev_command_trigger_{nullptr};
|
||||
Trigger<float> *position_trigger_;
|
||||
|
|
|
@ -51,6 +51,7 @@ void TimeBasedCover::loop() {
|
|||
float TimeBasedCover::get_setup_priority() const { return setup_priority::DATA; }
|
||||
CoverTraits TimeBasedCover::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_toggle(true);
|
||||
traits.set_is_assumed_state(this->assumed_state_);
|
||||
|
|
|
@ -128,6 +128,7 @@ void TuyaCover::dump_config() {
|
|||
|
||||
cover::CoverTraits TuyaCover::get_traits() {
|
||||
auto traits = cover::CoverTraits();
|
||||
traits.set_supports_stop(true);
|
||||
traits.set_supports_position(true);
|
||||
return traits;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,10 @@ void VoiceAssistant::start(struct sockaddr_storage *addr, uint16_t port) {
|
|||
|
||||
void VoiceAssistant::request_start() {
|
||||
ESP_LOGD(TAG, "Requesting start...");
|
||||
api::global_api_server->start_voice_assistant();
|
||||
if (!api::global_api_server->start_voice_assistant()) {
|
||||
ESP_LOGW(TAG, "Could not request start.");
|
||||
this->error_trigger_->trigger("not-connected", "Could not request start.");
|
||||
}
|
||||
}
|
||||
|
||||
void VoiceAssistant::signal_stop() {
|
||||
|
|
|
@ -252,6 +252,7 @@ def _validate(config):
|
|||
|
||||
|
||||
CONF_OUTPUT_POWER = "output_power"
|
||||
CONF_PASSIVE_SCAN = "passive_scan"
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
|
@ -280,6 +281,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
cv.SplitDefault(CONF_ENABLE_RRM, esp32_idf=False): cv.All(
|
||||
cv.boolean, cv.only_with_esp_idf
|
||||
),
|
||||
cv.Optional(CONF_PASSIVE_SCAN, default=False): cv.boolean,
|
||||
cv.Optional("enable_mdns"): cv.invalid(
|
||||
"This option has been removed. Please use the [disabled] option under the "
|
||||
"new mdns component instead."
|
||||
|
@ -379,6 +381,7 @@ async def to_code(config):
|
|||
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||
cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE]))
|
||||
cg.add(var.set_fast_connect(config[CONF_FAST_CONNECT]))
|
||||
cg.add(var.set_passive_scan(config[CONF_PASSIVE_SCAN]))
|
||||
if CONF_OUTPUT_POWER in config:
|
||||
cg.add(var.set_output_power(config[CONF_OUTPUT_POWER]))
|
||||
|
||||
|
|
|
@ -385,7 +385,7 @@ void WiFiComponent::print_connect_params_() {
|
|||
void WiFiComponent::start_scanning() {
|
||||
this->action_started_ = millis();
|
||||
ESP_LOGD(TAG, "Starting scan...");
|
||||
this->wifi_scan_start_();
|
||||
this->wifi_scan_start_(this->passive_scan_);
|
||||
this->state_ = WIFI_COMPONENT_STATE_STA_SCANNING;
|
||||
}
|
||||
|
||||
|
@ -615,6 +615,8 @@ bool WiFiComponent::is_connected() {
|
|||
}
|
||||
void WiFiComponent::set_power_save_mode(WiFiPowerSaveMode power_save) { this->power_save_ = power_save; }
|
||||
|
||||
void WiFiComponent::set_passive_scan(bool passive) { this->passive_scan_ = passive; }
|
||||
|
||||
std::string WiFiComponent::format_mac_addr(const uint8_t *mac) {
|
||||
char buf[20];
|
||||
sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
|
|
@ -217,6 +217,8 @@ class WiFiComponent : public Component {
|
|||
void set_power_save_mode(WiFiPowerSaveMode power_save);
|
||||
void set_output_power(float output_power) { output_power_ = output_power; }
|
||||
|
||||
void set_passive_scan(bool passive);
|
||||
|
||||
void save_wifi_sta(const std::string &ssid, const std::string &password);
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
|
@ -294,7 +296,7 @@ class WiFiComponent : public Component {
|
|||
bool wifi_sta_connect_(const WiFiAP &ap);
|
||||
void wifi_pre_setup_();
|
||||
WiFiSTAConnectStatus wifi_sta_connect_status_();
|
||||
bool wifi_scan_start_();
|
||||
bool wifi_scan_start_(bool passive);
|
||||
bool wifi_ap_ip_config_(optional<ManualIP> manual_ip);
|
||||
bool wifi_start_ap_(const WiFiAP &ap);
|
||||
bool wifi_disconnect_();
|
||||
|
@ -349,6 +351,7 @@ class WiFiComponent : public Component {
|
|||
bool scan_done_{false};
|
||||
bool ap_setup_{false};
|
||||
optional<float> output_power_;
|
||||
bool passive_scan_{false};
|
||||
ESPPreferenceObject pref_;
|
||||
bool has_saved_wifi_settings_{false};
|
||||
#ifdef USE_WIFI_11KV_SUPPORT
|
||||
|
|
|
@ -618,13 +618,13 @@ WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
|
|||
}
|
||||
return WiFiSTAConnectStatus::IDLE;
|
||||
}
|
||||
bool WiFiComponent::wifi_scan_start_() {
|
||||
bool WiFiComponent::wifi_scan_start_(bool passive) {
|
||||
// enable STA
|
||||
if (!this->wifi_mode_(true, {}))
|
||||
return false;
|
||||
|
||||
// need to use WiFi because of WiFiScanClass allocations :(
|
||||
int16_t err = WiFi.scanNetworks(true, true, false, 200);
|
||||
int16_t err = WiFi.scanNetworks(true, true, passive, 200);
|
||||
if (err != WIFI_SCAN_RUNNING) {
|
||||
ESP_LOGV(TAG, "WiFi.scanNetworks failed! %d", err);
|
||||
return false;
|
||||
|
|
|
@ -601,7 +601,7 @@ WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
|
|||
return WiFiSTAConnectStatus::IDLE;
|
||||
}
|
||||
}
|
||||
bool WiFiComponent::wifi_scan_start_() {
|
||||
bool WiFiComponent::wifi_scan_start_(bool passive) {
|
||||
static bool first_scan = false;
|
||||
|
||||
// enable STA
|
||||
|
@ -615,13 +615,21 @@ bool WiFiComponent::wifi_scan_start_() {
|
|||
config.channel = 0;
|
||||
config.show_hidden = 1;
|
||||
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 4, 0)
|
||||
config.scan_type = WIFI_SCAN_TYPE_ACTIVE;
|
||||
config.scan_type = passive ? WIFI_SCAN_TYPE_PASSIVE : WIFI_SCAN_TYPE_ACTIVE;
|
||||
if (first_scan) {
|
||||
config.scan_time.active.min = 100;
|
||||
config.scan_time.active.max = 200;
|
||||
if (passive) {
|
||||
config.scan_time.passive = 200;
|
||||
} else {
|
||||
config.scan_time.active.min = 100;
|
||||
config.scan_time.active.max = 200;
|
||||
}
|
||||
} else {
|
||||
config.scan_time.active.min = 400;
|
||||
config.scan_time.active.max = 500;
|
||||
if (passive) {
|
||||
config.scan_time.passive = 500;
|
||||
} else {
|
||||
config.scan_time.active.min = 400;
|
||||
config.scan_time.active.max = 500;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
first_scan = false;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#ifdef USE_WIFI_WPA2_EAP
|
||||
#include <esp_wpa2.h>
|
||||
#endif
|
||||
#include "dhcpserver/dhcpserver.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
|
@ -32,7 +33,7 @@ namespace wifi {
|
|||
static const char *const TAG = "wifi_esp32";
|
||||
|
||||
static EventGroupHandle_t s_wifi_event_group; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static xQueueHandle s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static QueueHandle_t s_event_queue; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static esp_netif_t *s_sta_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool s_sta_started = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
@ -414,17 +415,17 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
|||
if (!this->wifi_mode_(true, {}))
|
||||
return false;
|
||||
|
||||
tcpip_adapter_dhcp_status_t dhcp_status;
|
||||
esp_err_t err = tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dhcp_status);
|
||||
esp_netif_dhcp_status_t dhcp_status;
|
||||
esp_err_t err = esp_netif_dhcpc_get_status(s_sta_netif, &dhcp_status);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_dhcpc_get_status failed: %s", esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "esp_netif_dhcpc_get_status failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!manual_ip.has_value()) {
|
||||
// Use DHCP client
|
||||
if (dhcp_status != TCPIP_ADAPTER_DHCP_STARTED) {
|
||||
err = tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA);
|
||||
// No manual IP is set; use DHCP client
|
||||
if (dhcp_status != ESP_NETIF_DHCP_STARTED) {
|
||||
err = esp_netif_dhcpc_start(s_sta_netif);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "Starting DHCP client failed! %d", err);
|
||||
}
|
||||
|
@ -433,43 +434,29 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
|||
return true;
|
||||
}
|
||||
|
||||
tcpip_adapter_ip_info_t info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
esp_netif_ip_info_t info; // struct of ip4_addr_t with ip, netmask, gw
|
||||
info.ip.addr = static_cast<uint32_t>(manual_ip->static_ip);
|
||||
info.gw.addr = static_cast<uint32_t>(manual_ip->gateway);
|
||||
info.netmask.addr = static_cast<uint32_t>(manual_ip->subnet);
|
||||
|
||||
err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
|
||||
err = esp_netif_dhcpc_stop(s_sta_netif);
|
||||
if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STOPPED) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_dhcpc_stop failed: %s", esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "esp_netif_dhcpc_stop failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info);
|
||||
err = esp_netif_set_ip_info(s_sta_netif, &info);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_set_ip_info failed: %s", esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "esp_netif_set_ip_info failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
ip_addr_t dns;
|
||||
#if LWIP_IPV6
|
||||
dns.type = IPADDR_TYPE_V4;
|
||||
#endif
|
||||
esp_netif_dns_info_t dns;
|
||||
if (uint32_t(manual_ip->dns1) != 0) {
|
||||
#if LWIP_IPV6
|
||||
dns.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns1);
|
||||
#else
|
||||
dns.addr = static_cast<uint32_t>(manual_ip->dns1);
|
||||
#endif
|
||||
dns_setserver(0, &dns);
|
||||
dns.ip.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns1);
|
||||
esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_MAIN, &dns);
|
||||
}
|
||||
if (uint32_t(manual_ip->dns2) != 0) {
|
||||
#if LWIP_IPV6
|
||||
dns.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns2);
|
||||
#else
|
||||
dns.addr = static_cast<uint32_t>(manual_ip->dns2);
|
||||
#endif
|
||||
dns_setserver(1, &dns);
|
||||
dns.ip.u_addr.ip4.addr = static_cast<uint32_t>(manual_ip->dns2);
|
||||
esp_netif_set_dns_info(s_sta_netif, ESP_NETIF_DNS_BACKUP, &dns);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -478,10 +465,10 @@ bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
|||
network::IPAddress WiFiComponent::wifi_sta_ip() {
|
||||
if (!this->has_sta())
|
||||
return {};
|
||||
tcpip_adapter_ip_info_t ip;
|
||||
esp_err_t err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip);
|
||||
esp_netif_ip_info_t ip;
|
||||
esp_err_t err = esp_netif_get_ip_info(s_sta_netif, &ip);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_get_ip_info failed: %s", esp_err_to_name(err));
|
||||
ESP_LOGV(TAG, "esp_netif_get_ip_info failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
return {ip.ip.addr};
|
||||
|
@ -601,9 +588,9 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
|||
if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_START) {
|
||||
ESP_LOGV(TAG, "Event: WiFi STA start");
|
||||
// apply hostname
|
||||
err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, App.get_name().c_str());
|
||||
err = esp_netif_set_hostname(s_sta_netif, App.get_name().c_str());
|
||||
if (err != ERR_OK) {
|
||||
ESP_LOGW(TAG, "tcpip_adapter_set_hostname failed: %s", esp_err_to_name(err));
|
||||
ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
s_sta_started = true;
|
||||
|
@ -651,7 +638,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
|||
} else if (data->event_base == IP_EVENT && data->event_id == IP_EVENT_STA_GOT_IP) {
|
||||
const auto &it = data->data.ip_got_ip;
|
||||
#if LWIP_IPV6_AUTOCONFIG
|
||||
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
|
||||
esp_netif_create_ip6_linklocal(s_sta_netif);
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Event: Got IP static_ip=%s gateway=%s", format_ip4_addr(it.ip_info.ip).c_str(),
|
||||
format_ip4_addr(it.ip_info.gw).c_str());
|
||||
|
@ -736,7 +723,7 @@ WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
|
|||
}
|
||||
return WiFiSTAConnectStatus::IDLE;
|
||||
}
|
||||
bool WiFiComponent::wifi_scan_start_() {
|
||||
bool WiFiComponent::wifi_scan_start_(bool passive) {
|
||||
// enable STA
|
||||
if (!this->wifi_mode_(true, {}))
|
||||
return false;
|
||||
|
@ -746,9 +733,13 @@ bool WiFiComponent::wifi_scan_start_() {
|
|||
config.bssid = nullptr;
|
||||
config.channel = 0;
|
||||
config.show_hidden = true;
|
||||
config.scan_type = WIFI_SCAN_TYPE_ACTIVE;
|
||||
config.scan_time.active.min = 100;
|
||||
config.scan_time.active.max = 300;
|
||||
config.scan_type = passive ? WIFI_SCAN_TYPE_PASSIVE : WIFI_SCAN_TYPE_ACTIVE;
|
||||
if (passive) {
|
||||
config.scan_time.passive = 300;
|
||||
} else {
|
||||
config.scan_time.active.min = 100;
|
||||
config.scan_time.active.max = 300;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_wifi_scan_start(&config, false);
|
||||
if (err != ESP_OK) {
|
||||
|
@ -766,8 +757,7 @@ bool WiFiComponent::wifi_ap_ip_config_(optional<ManualIP> manual_ip) {
|
|||
if (!this->wifi_mode_({}, true))
|
||||
return false;
|
||||
|
||||
tcpip_adapter_ip_info_t info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
esp_netif_ip_info_t info;
|
||||
if (manual_ip.has_value()) {
|
||||
info.ip.addr = static_cast<uint32_t>(manual_ip->static_ip);
|
||||
info.gw.addr = static_cast<uint32_t>(manual_ip->gateway);
|
||||
|
@ -777,17 +767,17 @@ bool WiFiComponent::wifi_ap_ip_config_(optional<ManualIP> manual_ip) {
|
|||
info.gw.addr = static_cast<uint32_t>(network::IPAddress(192, 168, 4, 1));
|
||||
info.netmask.addr = static_cast<uint32_t>(network::IPAddress(255, 255, 255, 0));
|
||||
}
|
||||
tcpip_adapter_dhcp_status_t dhcp_status;
|
||||
tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status);
|
||||
err = tcpip_adapter_dhcps_stop(TCPIP_ADAPTER_IF_AP);
|
||||
esp_netif_dhcp_status_t dhcp_status;
|
||||
esp_netif_dhcps_get_status(s_sta_netif, &dhcp_status);
|
||||
err = esp_netif_dhcps_stop(s_sta_netif);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_dhcps_stop failed! %d", err);
|
||||
ESP_LOGV(TAG, "esp_netif_dhcps_stop failed! %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_AP, &info);
|
||||
err = esp_netif_set_ip_info(s_sta_netif, &info);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_set_ip_info failed! %d", err);
|
||||
ESP_LOGV(TAG, "esp_netif_set_ip_info failed! %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -800,17 +790,17 @@ bool WiFiComponent::wifi_ap_ip_config_(optional<ManualIP> manual_ip) {
|
|||
start_address[3] += 100;
|
||||
lease.end_ip.addr = static_cast<uint32_t>(start_address);
|
||||
ESP_LOGV(TAG, "DHCP server IP lease end: %s", start_address.str().c_str());
|
||||
err = tcpip_adapter_dhcps_option(TCPIP_ADAPTER_OP_SET, TCPIP_ADAPTER_REQUESTED_IP_ADDRESS, &lease, sizeof(lease));
|
||||
err = esp_netif_dhcps_option(s_sta_netif, ESP_NETIF_OP_SET, ESP_NETIF_REQUESTED_IP_ADDRESS, &lease, sizeof(lease));
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_dhcps_option failed! %d", err);
|
||||
ESP_LOGV(TAG, "esp_netif_dhcps_option failed! %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = tcpip_adapter_dhcps_start(TCPIP_ADAPTER_IF_AP);
|
||||
err = esp_netif_dhcps_start(s_sta_netif);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGV(TAG, "tcpip_adapter_dhcps_start failed! %d", err);
|
||||
ESP_LOGV(TAG, "esp_netif_dhcps_start failed! %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -856,8 +846,8 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
|
|||
return true;
|
||||
}
|
||||
network::IPAddress WiFiComponent::wifi_soft_ap_ip() {
|
||||
tcpip_adapter_ip_info_t ip;
|
||||
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip);
|
||||
esp_netif_ip_info_t ip;
|
||||
esp_netif_get_ip_info(s_sta_netif, &ip);
|
||||
return {ip.ip.addr};
|
||||
}
|
||||
bool WiFiComponent::wifi_disconnect_() { return esp_wifi_disconnect(); }
|
||||
|
|
|
@ -125,10 +125,11 @@ void WiFiComponent::wifi_scan_result(void *env, const cyw43_ev_scan_result_t *re
|
|||
}
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_scan_start_() {
|
||||
bool WiFiComponent::wifi_scan_start_(bool passive) {
|
||||
this->scan_result_.clear();
|
||||
this->scan_done_ = false;
|
||||
cyw43_wifi_scan_options_t scan_options = {0};
|
||||
scan_options.scan_type = passive ? 1 : 0;
|
||||
int err = cyw43_wifi_scan(&cyw43_state, &scan_options, nullptr, &s_wifi_scan_result);
|
||||
if (err) {
|
||||
ESP_LOGV(TAG, "cyw43_wifi_scan failed!");
|
||||
|
|
|
@ -72,6 +72,16 @@ void EntityBase::calc_object_id_() {
|
|||
this->object_id_hash_ = fnv1_hash(this->object_id_c_str_);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; }
|
||||
|
||||
std::string EntityBase_DeviceClass::get_device_class() {
|
||||
if (this->device_class_ == nullptr) {
|
||||
return "";
|
||||
}
|
||||
return this->device_class_;
|
||||
}
|
||||
|
||||
void EntityBase_DeviceClass::set_device_class(const char *device_class) { this->device_class_ = device_class; }
|
||||
|
||||
} // namespace esphome
|
||||
|
|
|
@ -63,4 +63,15 @@ class EntityBase {
|
|||
EntityCategory entity_category_{ENTITY_CATEGORY_NONE};
|
||||
};
|
||||
|
||||
class EntityBase_DeviceClass {
|
||||
public:
|
||||
/// Get the device class, using the manual override if set.
|
||||
std::string get_device_class();
|
||||
/// Manually set the device class.
|
||||
void set_device_class(const char *device_class);
|
||||
|
||||
protected:
|
||||
const char *device_class_{nullptr}; ///< Device class override
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#elif defined(USE_ESP32_FRAMEWORK_ARDUINO)
|
||||
#include <Esp.h>
|
||||
#elif defined(USE_ESP_IDF)
|
||||
#include "esp_mac.h"
|
||||
#include "esp_random.h"
|
||||
#include "esp_system.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/portmacro.h>
|
||||
|
|
|
@ -80,9 +80,9 @@ build_flags =
|
|||
; This are common settings for the ESP8266 using Arduino.
|
||||
[common:esp8266-arduino]
|
||||
extends = common:arduino
|
||||
platform = platformio/espressif8266 @ 3.2.0
|
||||
platform = platformio/espressif8266@3.2.0
|
||||
platform_packages =
|
||||
platformio/framework-arduinoespressif8266 @ ~3.30002.0
|
||||
platformio/framework-arduinoespressif8266@~3.30002.0
|
||||
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
|
@ -104,9 +104,9 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script
|
|||
; This are common settings for the ESP32 (all variants) using Arduino.
|
||||
[common:esp32-arduino]
|
||||
extends = common:arduino
|
||||
platform = platformio/espressif32 @ 5.2.0
|
||||
platform = platformio/espressif32@5.3.0
|
||||
platform_packages =
|
||||
platformio/framework-arduinoespressif32 @ ~3.20005.0
|
||||
platformio/framework-arduinoespressif32@~3.20005.0
|
||||
|
||||
framework = arduino
|
||||
board = nodemcu-32s
|
||||
|
@ -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.
|
||||
[common:esp32-idf]
|
||||
extends = common:idf
|
||||
platform = platformio/espressif32 @ 5.3.0
|
||||
platform = platformio/espressif32@5.3.0
|
||||
platform_packages =
|
||||
platformio/framework-espidf @ ~3.40404.0
|
||||
platformio/framework-espidf@~3.40404.0
|
||||
|
||||
framework = espidf
|
||||
lib_deps =
|
||||
|
@ -157,8 +157,8 @@ board_build.filesystem_size = 0.5m
|
|||
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
platform_packages =
|
||||
; earlephilhower/framework-arduinopico @ ~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted
|
||||
earlephilhower/framework-arduinopico @ https://github.com/earlephilhower/arduino-pico/releases/download/2.6.2/rp2040-2.6.2.zip
|
||||
; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted
|
||||
earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/2.6.2/rp2040-2.6.2.zip
|
||||
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
|
|
|
@ -2,7 +2,7 @@ voluptuous==0.13.1
|
|||
PyYAML==6.0
|
||||
paho-mqtt==1.6.1
|
||||
colorama==0.4.6
|
||||
tornado==6.2
|
||||
tornado==6.3.1
|
||||
tzlocal==4.2 # from time
|
||||
tzdata>=2021.1 # from time
|
||||
pyserial==3.5
|
||||
|
@ -10,9 +10,12 @@ platformio==6.1.6 # When updating platformio, also update Dockerfile
|
|||
esptool==4.5.1
|
||||
click==8.1.3
|
||||
esphome-dashboard==20230214.0
|
||||
aioesphomeapi==13.7.0
|
||||
aioesphomeapi==13.7.1
|
||||
zeroconf==0.56.0
|
||||
|
||||
# esp-idf requires this, but doesn't bundle it by default
|
||||
# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24
|
||||
kconfiglib==13.7.1
|
||||
|
||||
# esp-idf >= 5.0 requires this
|
||||
pyparsing >= 3.0
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
pylint==2.17.2
|
||||
pylint==2.17.3
|
||||
flake8==6.0.0 # also change in .pre-commit-config.yaml when updating
|
||||
black==23.3.0 # also change in .pre-commit-config.yaml when updating
|
||||
pyupgrade==3.3.1 # also change in .pre-commit-config.yaml when updating
|
||||
pre-commit
|
||||
|
||||
# Unit tests
|
||||
pytest==7.3.0
|
||||
pytest==7.3.1
|
||||
pytest-cov==4.0.0
|
||||
pytest-mock==3.10.0
|
||||
pytest-asyncio==0.21.0
|
||||
|
|
48
script/platformio_install_deps.py
Executable file
48
script/platformio_install_deps.py
Executable file
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env python3
|
||||
# This script is used to preinstall
|
||||
# all platformio libraries in the global storage
|
||||
|
||||
import configparser
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
config = configparser.ConfigParser(inline_comment_prefixes=(";",))
|
||||
config.read(sys.argv[1])
|
||||
|
||||
libs = []
|
||||
tools = []
|
||||
platforms = []
|
||||
# Extract from every lib_deps key in all sections
|
||||
for section in config.sections():
|
||||
conf = config[section]
|
||||
if "lib_deps" in conf:
|
||||
for lib_dep in conf["lib_deps"].splitlines():
|
||||
if not lib_dep:
|
||||
# Empty line or comment
|
||||
continue
|
||||
if lib_dep.startswith("${"):
|
||||
# Extending from another section
|
||||
continue
|
||||
if "@" not in lib_dep:
|
||||
# No version pinned, this is an internal lib
|
||||
continue
|
||||
libs.append("-l")
|
||||
libs.append(lib_dep)
|
||||
if "platform" in conf:
|
||||
platforms.append("-p")
|
||||
platforms.append(conf["platform"])
|
||||
if "platform_packages" in conf:
|
||||
for tool in conf["platform_packages"].splitlines():
|
||||
if not tool:
|
||||
# Empty line or comment
|
||||
continue
|
||||
if tool.startswith("${"):
|
||||
# Extending from another section
|
||||
continue
|
||||
if tool.find("https://github.com") != -1:
|
||||
split = tool.find("@")
|
||||
tool = tool[split + 1 :]
|
||||
tools.append("-t")
|
||||
tools.append(tool)
|
||||
|
||||
subprocess.check_call(["platformio", "pkg", "install", "-g", *libs, *platforms, *tools])
|
|
@ -14,3 +14,5 @@ pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_te
|
|||
pip3 install --no-use-pep517 -e .
|
||||
|
||||
pre-commit install
|
||||
|
||||
script/platformio_install_deps.py platformio.ini
|
||||
|
|
|
@ -1247,6 +1247,13 @@ sensor:
|
|||
temperature:
|
||||
name: Max9611 Temp
|
||||
update_interval: 1s
|
||||
- platform: mlx90614
|
||||
i2c_id: i2c_bus
|
||||
ambient:
|
||||
name: Ambient
|
||||
object:
|
||||
name: Object
|
||||
emissivity: 1.0
|
||||
- platform: mpl3115a2
|
||||
i2c_id: i2c_bus
|
||||
temperature:
|
||||
|
|
Loading…
Reference in a new issue