mirror of
https://github.com/esphome/esphome.git
synced 2025-04-14 23:00:29 +01:00
Merge branch 'dev' into bh1745
This commit is contained in:
commit
c81df06448
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@ -96,12 +96,12 @@ jobs:
|
|||||||
uses: docker/setup-qemu-action@v3.0.0
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v3.1.0
|
uses: docker/login-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v3.1.0
|
uses: docker/login-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@ -188,13 +188,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
if: matrix.registry == 'dockerhub'
|
||||||
uses: docker/login-action@v3.1.0
|
uses: docker/login-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
if: matrix.registry == 'ghcr'
|
if: matrix.registry == 'ghcr'
|
||||||
uses: docker/login-action@v3.1.0
|
uses: docker/login-action@v3.2.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
2
.github/workflows/sync-device-classes.yml
vendored
2
.github/workflows/sync-device-classes.yml
vendored
@ -36,7 +36,7 @@ jobs:
|
|||||||
python ./script/sync-device_class.py
|
python ./script/sync-device_class.py
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: peter-evans/create-pull-request@v6.0.4
|
uses: peter-evans/create-pull-request@v6.0.5
|
||||||
with:
|
with:
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||||
committer: esphomebot <esphome@nabucasa.com>
|
committer: esphomebot <esphome@nabucasa.com>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||||
rev: 24.2.0
|
rev: 24.4.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args:
|
args:
|
||||||
|
@ -211,6 +211,7 @@ esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
|||||||
esphome/components/lock/* @esphome/core
|
esphome/components/lock/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
esphome/components/ltr390/* @sjtrny
|
esphome/components/ltr390/* @sjtrny
|
||||||
|
esphome/components/ltr_als_ps/* @latonita
|
||||||
esphome/components/matrix_keypad/* @ssieb
|
esphome/components/matrix_keypad/* @ssieb
|
||||||
esphome/components/max31865/* @DAVe3283
|
esphome/components/max31865/* @DAVe3283
|
||||||
esphome/components/max44009/* @berfenger
|
esphome/components/max44009/* @berfenger
|
||||||
@ -415,7 +416,7 @@ esphome/components/veml3235/* @kbx81
|
|||||||
esphome/components/veml7700/* @latonita
|
esphome/components/veml7700/* @latonita
|
||||||
esphome/components/version/* @esphome/core
|
esphome/components/version/* @esphome/core
|
||||||
esphome/components/voice_assistant/* @jesserockz
|
esphome/components/voice_assistant/* @jesserockz
|
||||||
esphome/components/wake_on_lan/* @willwill2will54
|
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
|
||||||
esphome/components/waveshare_epaper/* @clydebarrow
|
esphome/components/waveshare_epaper/* @clydebarrow
|
||||||
esphome/components/web_server_base/* @OttoWinter
|
esphome/components/web_server_base/* @OttoWinter
|
||||||
esphome/components/web_server_idf/* @dentra
|
esphome/components/web_server_idf/* @dentra
|
||||||
|
@ -4,11 +4,11 @@ from esphome.const import (
|
|||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
ICON_ARROW_EXPAND_VERTICAL,
|
ICON_ARROW_EXPAND_VERTICAL,
|
||||||
DEVICE_CLASS_DISTANCE,
|
DEVICE_CLASS_DISTANCE,
|
||||||
|
UNIT_MILLIMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@TH-Braemer"]
|
CODEOWNERS = ["@TH-Braemer"]
|
||||||
DEPENDENCIES = ["uart"]
|
DEPENDENCIES = ["uart"]
|
||||||
UNIT_MILLIMETERS = "mm"
|
|
||||||
|
|
||||||
a02yyuw_ns = cg.esphome_ns.namespace("a02yyuw")
|
a02yyuw_ns = cg.esphome_ns.namespace("a02yyuw")
|
||||||
A02yyuwComponent = a02yyuw_ns.class_(
|
A02yyuwComponent = a02yyuw_ns.class_(
|
||||||
@ -17,7 +17,7 @@ A02yyuwComponent = a02yyuw_ns.class_(
|
|||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||||
A02yyuwComponent,
|
A02yyuwComponent,
|
||||||
unit_of_measurement=UNIT_MILLIMETERS,
|
unit_of_measurement=UNIT_MILLIMETER,
|
||||||
icon=ICON_ARROW_EXPAND_VERTICAL,
|
icon=ICON_ARROW_EXPAND_VERTICAL,
|
||||||
accuracy_decimals=0,
|
accuracy_decimals=0,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include "ade7880_registers.h"
|
#include "ade7880_registers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ade7880 {
|
namespace ade7880 {
|
||||||
|
|
||||||
@ -156,7 +158,7 @@ void ADE7880::update() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "update took %u ms", millis() - start);
|
ESP_LOGD(TAG, "update took %" PRIu32 " ms", millis() - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADE7880::dump_config() {
|
void ADE7880::dump_config() {
|
||||||
@ -176,9 +178,9 @@ void ADE7880::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy);
|
LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy);
|
||||||
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
|
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
|
||||||
ESP_LOGCONFIG(TAG, " Calibration:");
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_a_->current_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_a_->current_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Voltage: %d", this->channel_a_->voltage_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Power: %d", this->channel_a_->power_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_a_->power_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_a_->phase_angle_calibration);
|
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_a_->phase_angle_calibration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,9 +194,9 @@ void ADE7880::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy);
|
LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy);
|
||||||
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
|
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
|
||||||
ESP_LOGCONFIG(TAG, " Calibration:");
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_b_->current_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_b_->current_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Voltage: %d", this->channel_b_->voltage_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Power: %d", this->channel_b_->power_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_b_->power_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_b_->phase_angle_calibration);
|
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_b_->phase_angle_calibration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,9 +210,9 @@ void ADE7880::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy);
|
LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy);
|
||||||
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
|
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
|
||||||
ESP_LOGCONFIG(TAG, " Calibration:");
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_c_->current_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_c_->current_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Voltage: %d", this->channel_c_->voltage_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Power: %d", this->channel_c_->power_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_c_->power_gain_calibration);
|
||||||
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_c_->phase_angle_calibration);
|
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_c_->phase_angle_calibration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +220,7 @@ void ADE7880::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Neutral:");
|
ESP_LOGCONFIG(TAG, " Neutral:");
|
||||||
LOG_SENSOR(" ", "Current", this->channel_n_->current);
|
LOG_SENSOR(" ", "Current", this->channel_n_->current);
|
||||||
ESP_LOGCONFIG(TAG, " Calibration:");
|
ESP_LOGCONFIG(TAG, " Calibration:");
|
||||||
ESP_LOGCONFIG(TAG, " Current: %u", this->channel_n_->current_gain_calibration);
|
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_n_->current_gain_calibration);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_I2C_DEVICE(this);
|
LOG_I2C_DEVICE(this);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "ade7953_base.h"
|
#include "ade7953_base.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ade7953_base {
|
namespace ade7953_base {
|
||||||
|
|
||||||
@ -105,7 +107,7 @@ void ADE7953::update() {
|
|||||||
this->last_update_ = now;
|
this->last_update_ = now;
|
||||||
// prevent DIV/0
|
// prevent DIV/0
|
||||||
pf = ADE_WATTSEC_POWER_FACTOR * (diff < 10 ? 10 : diff) / 1000;
|
pf = ADE_WATTSEC_POWER_FACTOR * (diff < 10 ? 10 : diff) / 1000;
|
||||||
ESP_LOGVV(TAG, "ADE7953::update() diff=%d pf=%f", diff, pf);
|
ESP_LOGVV(TAG, "ADE7953::update() diff=%" PRIu32 " pf=%f", diff, pf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apparent power
|
// Apparent power
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "ags10.h"
|
#include "ags10.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ags10 {
|
namespace ags10 {
|
||||||
static const char *const TAG = "ags10";
|
static const char *const TAG = "ags10";
|
||||||
@ -35,7 +37,7 @@ void AGS10Component::setup() {
|
|||||||
|
|
||||||
auto resistance = this->read_resistance_();
|
auto resistance = this->read_resistance_();
|
||||||
if (resistance) {
|
if (resistance) {
|
||||||
ESP_LOGD(TAG, "AGS10 Sensor Resistance: 0x%08X", *resistance);
|
ESP_LOGD(TAG, "AGS10 Sensor Resistance: 0x%08" PRIX32, *resistance);
|
||||||
if (this->resistance_ != nullptr) {
|
if (this->resistance_ != nullptr) {
|
||||||
this->resistance_->publish_state(*resistance);
|
this->resistance_->publish_state(*resistance);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import web_server
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import maybe_simple_id
|
from esphome.automation import maybe_simple_id
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
@ -8,6 +9,7 @@ from esphome.const import (
|
|||||||
CONF_ON_STATE,
|
CONF_ON_STATE,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_CODE,
|
CONF_CODE,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
)
|
)
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
|
|
||||||
@ -76,6 +78,8 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_(
|
|||||||
)
|
)
|
||||||
|
|
||||||
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
|
web_server.WEBSERVER_SORTING_SCHEMA
|
||||||
|
).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(AlarmControlPanel),
|
cv.GenerateID(): cv.declare_id(AlarmControlPanel),
|
||||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
@ -185,6 +189,9 @@ async def setup_alarm_control_panel_core_(var, config):
|
|||||||
for conf in config.get(CONF_ON_READY, []):
|
for conf in config.get(CONF_ON_READY, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_alarm_control_panel(var, config):
|
async def register_alarm_control_panel(var, config):
|
||||||
|
@ -1517,6 +1517,25 @@ message VoiceAssistantAudio {
|
|||||||
bool end = 2;
|
bool end = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum VoiceAssistantTimerEvent {
|
||||||
|
VOICE_ASSISTANT_TIMER_STARTED = 0;
|
||||||
|
VOICE_ASSISTANT_TIMER_UPDATED = 1;
|
||||||
|
VOICE_ASSISTANT_TIMER_CANCELLED = 2;
|
||||||
|
VOICE_ASSISTANT_TIMER_FINISHED = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantTimerEventResponse {
|
||||||
|
option (id) = 115;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
|
VoiceAssistantTimerEvent event_type = 1;
|
||||||
|
string timer_id = 2;
|
||||||
|
string name = 3;
|
||||||
|
uint32 total_seconds = 4;
|
||||||
|
uint32 seconds_left = 5;
|
||||||
|
bool is_active = 6;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== ALARM CONTROL PANEL ====================
|
// ==================== ALARM CONTROL PANEL ====================
|
||||||
enum AlarmControlPanelState {
|
enum AlarmControlPanelState {
|
||||||
|
@ -1193,6 +1193,15 @@ void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
|
|||||||
voice_assistant::global_voice_assistant->on_audio(msg);
|
voice_assistant::global_voice_assistant->on_audio(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
void APIConnection::on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) {
|
||||||
|
if (voice_assistant::global_voice_assistant != nullptr) {
|
||||||
|
if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice_assistant::global_voice_assistant->on_timer_event(msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -150,6 +150,7 @@ class APIConnection : public APIServerConnection {
|
|||||||
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
||||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||||
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
|
||||||
|
void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
@ -475,6 +475,22 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
template<> const char *proto_enum_to_string<enums::VoiceAssistantTimerEvent>(enums::VoiceAssistantTimerEvent value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_STARTED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_STARTED";
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_UPDATED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_UPDATED";
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_CANCELLED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_CANCELLED";
|
||||||
|
case enums::VOICE_ASSISTANT_TIMER_FINISHED:
|
||||||
|
return "VOICE_ASSISTANT_TIMER_FINISHED";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
|
template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::ALARM_STATE_DISARMED:
|
case enums::ALARM_STATE_DISARMED:
|
||||||
@ -6857,6 +6873,82 @@ void VoiceAssistantAudio::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->event_type = value.as_enum<enums::VoiceAssistantTimerEvent>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->total_seconds = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->seconds_left = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
this->is_active = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool VoiceAssistantTimerEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->timer_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->name = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void VoiceAssistantTimerEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_enum<enums::VoiceAssistantTimerEvent>(1, this->event_type);
|
||||||
|
buffer.encode_string(2, this->timer_id);
|
||||||
|
buffer.encode_string(3, this->name);
|
||||||
|
buffer.encode_uint32(4, this->total_seconds);
|
||||||
|
buffer.encode_uint32(5, this->seconds_left);
|
||||||
|
buffer.encode_bool(6, this->is_active);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void VoiceAssistantTimerEventResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("VoiceAssistantTimerEventResponse {\n");
|
||||||
|
out.append(" event_type: ");
|
||||||
|
out.append(proto_enum_to_string<enums::VoiceAssistantTimerEvent>(this->event_type));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" timer_id: ");
|
||||||
|
out.append("'").append(this->timer_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" name: ");
|
||||||
|
out.append("'").append(this->name).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" total_seconds: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->total_seconds);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" seconds_left: ");
|
||||||
|
sprintf(buffer, "%" PRIu32, this->seconds_left);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" is_active: ");
|
||||||
|
out.append(YESNO(this->is_active));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 6: {
|
case 6: {
|
||||||
|
@ -191,6 +191,12 @@ enum VoiceAssistantEvent : uint32_t {
|
|||||||
VOICE_ASSISTANT_TTS_STREAM_START = 98,
|
VOICE_ASSISTANT_TTS_STREAM_START = 98,
|
||||||
VOICE_ASSISTANT_TTS_STREAM_END = 99,
|
VOICE_ASSISTANT_TTS_STREAM_END = 99,
|
||||||
};
|
};
|
||||||
|
enum VoiceAssistantTimerEvent : uint32_t {
|
||||||
|
VOICE_ASSISTANT_TIMER_STARTED = 0,
|
||||||
|
VOICE_ASSISTANT_TIMER_UPDATED = 1,
|
||||||
|
VOICE_ASSISTANT_TIMER_CANCELLED = 2,
|
||||||
|
VOICE_ASSISTANT_TIMER_FINISHED = 3,
|
||||||
|
};
|
||||||
enum AlarmControlPanelState : uint32_t {
|
enum AlarmControlPanelState : uint32_t {
|
||||||
ALARM_STATE_DISARMED = 0,
|
ALARM_STATE_DISARMED = 0,
|
||||||
ALARM_STATE_ARMED_HOME = 1,
|
ALARM_STATE_ARMED_HOME = 1,
|
||||||
@ -1775,6 +1781,23 @@ class VoiceAssistantAudio : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
|
class VoiceAssistantTimerEventResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
enums::VoiceAssistantTimerEvent event_type{};
|
||||||
|
std::string timer_id{};
|
||||||
|
std::string name{};
|
||||||
|
uint32_t total_seconds{0};
|
||||||
|
uint32_t seconds_left{0};
|
||||||
|
bool is_active{false};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{};
|
std::string object_id{};
|
||||||
|
@ -484,6 +484,8 @@ bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAud
|
|||||||
return this->send_message_<VoiceAssistantAudio>(msg, 106);
|
return this->send_message_<VoiceAssistantAudio>(msg, 106);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
||||||
const ListEntitiesAlarmControlPanelResponse &msg) {
|
const ListEntitiesAlarmControlPanelResponse &msg) {
|
||||||
@ -1093,6 +1095,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_date_time_command_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_date_time_command_request(msg);
|
this->on_date_time_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 115: {
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
VoiceAssistantTimerEventResponse msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_voice_assistant_timer_event_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_voice_assistant_timer_event_response(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -244,6 +244,9 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
|
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
|
||||||
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
|
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
|
virtual void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &value){};
|
||||||
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
||||||
#endif
|
#endif
|
||||||
|
@ -105,7 +105,7 @@ class CustomAPIDevice {
|
|||||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
*å
|
*
|
||||||
* ```cpp
|
* ```cpp
|
||||||
* void setup() override {
|
* void setup() override {
|
||||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
||||||
|
@ -4,7 +4,7 @@ from esphome.cpp_generator import MockObjClass
|
|||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
from esphome import automation, core
|
from esphome import automation, core
|
||||||
from esphome.automation import Condition, maybe_simple_id
|
from esphome.automation import Condition, maybe_simple_id
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_DELAY,
|
CONF_DELAY,
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
@ -27,6 +27,7 @@ from esphome.const import (
|
|||||||
CONF_TIMING,
|
CONF_TIMING,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
DEVICE_CLASS_BATTERY_CHARGING,
|
DEVICE_CLASS_BATTERY_CHARGING,
|
||||||
DEVICE_CLASS_CARBON_MONOXIDE,
|
DEVICE_CLASS_CARBON_MONOXIDE,
|
||||||
@ -385,70 +386,76 @@ def validate_click_timing(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
BINARY_SENSOR_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(BinarySensor),
|
.extend(cv.MQTT_COMPONENT_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
.extend(
|
||||||
mqtt.MQTTBinarySensorComponent
|
{
|
||||||
),
|
cv.GenerateID(): cv.declare_id(BinarySensor),
|
||||||
cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
mqtt.MQTTBinarySensorComponent
|
||||||
cv.Optional(CONF_FILTERS): validate_filters,
|
),
|
||||||
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
|
cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
|
||||||
{
|
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
|
cv.Optional(CONF_FILTERS): validate_filters,
|
||||||
}
|
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
|
||||||
),
|
|
||||||
cv.Optional(CONF_ON_RELEASE): automation.validate_automation(
|
|
||||||
{
|
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_ON_CLICK): cv.All(
|
|
||||||
automation.validate_automation(
|
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
|
||||||
cv.Optional(
|
|
||||||
CONF_MIN_LENGTH, default="50ms"
|
|
||||||
): cv.positive_time_period_milliseconds,
|
|
||||||
cv.Optional(
|
|
||||||
CONF_MAX_LENGTH, default="350ms"
|
|
||||||
): cv.positive_time_period_milliseconds,
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
validate_click_timing,
|
cv.Optional(CONF_ON_RELEASE): automation.validate_automation(
|
||||||
),
|
|
||||||
cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
|
|
||||||
automation.validate_automation(
|
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
|
||||||
cv.Optional(
|
|
||||||
CONF_MIN_LENGTH, default="50ms"
|
|
||||||
): cv.positive_time_period_milliseconds,
|
|
||||||
cv.Optional(
|
|
||||||
CONF_MAX_LENGTH, default="350ms"
|
|
||||||
): cv.positive_time_period_milliseconds,
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
validate_click_timing,
|
cv.Optional(CONF_ON_CLICK): cv.All(
|
||||||
),
|
automation.validate_automation(
|
||||||
cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
|
{
|
||||||
{
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger),
|
cv.Optional(
|
||||||
cv.Required(CONF_TIMING): cv.All(
|
CONF_MIN_LENGTH, default="50ms"
|
||||||
[parse_multi_click_timing_str], validate_multi_click_timing
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_MAX_LENGTH, default="350ms"
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(
|
validate_click_timing,
|
||||||
CONF_INVALID_COOLDOWN, default="1s"
|
),
|
||||||
): cv.positive_time_period_milliseconds,
|
cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
|
||||||
}
|
automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
{
|
DoubleClickTrigger
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
),
|
||||||
}
|
cv.Optional(
|
||||||
),
|
CONF_MIN_LENGTH, default="50ms"
|
||||||
}
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_MAX_LENGTH, default="350ms"
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
validate_click_timing,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger),
|
||||||
|
cv.Required(CONF_TIMING): cv.All(
|
||||||
|
[parse_multi_click_timing_str], validate_multi_click_timing
|
||||||
|
),
|
||||||
|
cv.Optional(
|
||||||
|
CONF_INVALID_COOLDOWN, default="1s"
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
@ -536,6 +543,10 @@ async def setup_binary_sensor_core_(var, config):
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_binary_sensor(var, config):
|
async def register_binary_sensor(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import maybe_simple_id
|
from esphome.automation import maybe_simple_id
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_ENTITY_CATEGORY,
|
CONF_ENTITY_CATEGORY,
|
||||||
@ -11,6 +11,7 @@ from esphome.const import (
|
|||||||
CONF_ON_PRESS,
|
CONF_ON_PRESS,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
DEVICE_CLASS_EMPTY,
|
DEVICE_CLASS_EMPTY,
|
||||||
DEVICE_CLASS_IDENTIFY,
|
DEVICE_CLASS_IDENTIFY,
|
||||||
DEVICE_CLASS_RESTART,
|
DEVICE_CLASS_RESTART,
|
||||||
@ -43,16 +44,20 @@ ButtonPressTrigger = button_ns.class_(
|
|||||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||||
|
|
||||||
|
|
||||||
BUTTON_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
BUTTON_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
.extend(
|
||||||
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
|
{
|
||||||
{
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
|
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||||
}
|
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
|
||||||
),
|
{
|
||||||
}
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
@ -92,6 +97,10 @@ async def setup_button_core_(var, config):
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_button(var, config):
|
async def register_button(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ACTION_STATE_TOPIC,
|
CONF_ACTION_STATE_TOPIC,
|
||||||
CONF_AWAY,
|
CONF_AWAY,
|
||||||
@ -44,6 +44,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_VISUAL,
|
CONF_VISUAL,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
|
||||||
@ -150,93 +151,97 @@ VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Any(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
CLIMATE_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(Climate),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
|
.extend(
|
||||||
cv.Optional(CONF_VISUAL, default={}): cv.Schema(
|
{
|
||||||
{
|
cv.GenerateID(): cv.declare_id(Climate),
|
||||||
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
|
||||||
cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
|
cv.Optional(CONF_VISUAL, default={}): cv.Schema(
|
||||||
cv.Optional(CONF_TEMPERATURE_STEP): VISUAL_TEMPERATURE_STEP_SCHEMA,
|
{
|
||||||
cv.Optional(CONF_MIN_HUMIDITY): cv.percentage_int,
|
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
|
||||||
cv.Optional(CONF_MAX_HUMIDITY): cv.percentage_int,
|
cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
|
||||||
}
|
cv.Optional(CONF_TEMPERATURE_STEP): VISUAL_TEMPERATURE_STEP_SCHEMA,
|
||||||
),
|
cv.Optional(CONF_MIN_HUMIDITY): cv.percentage_int,
|
||||||
cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_MAX_HUMIDITY): cv.percentage_int,
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_CURRENT_HUMIDITY_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_CURRENT_HUMIDITY_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_TARGET_HUMIDITY_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_CONTROL): automation.validate_automation(
|
cv.Optional(CONF_TARGET_HUMIDITY_STATE_TOPIC): cv.All(
|
||||||
{
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ControlTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_CONTROL): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ControlTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
}
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -403,6 +408,10 @@ async def setup_climate_core_(var, config):
|
|||||||
trigger, [(ClimateCall.operator("ref"), "x")], conf
|
trigger, [(ClimateCall.operator("ref"), "x")], conf
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_climate(var, config):
|
async def register_climate(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import maybe_simple_id, Condition
|
from esphome.automation import maybe_simple_id, Condition
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
@ -16,6 +16,7 @@ from esphome.const import (
|
|||||||
CONF_TILT_STATE_TOPIC,
|
CONF_TILT_STATE_TOPIC,
|
||||||
CONF_STOP,
|
CONF_STOP,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
DEVICE_CLASS_AWNING,
|
DEVICE_CLASS_AWNING,
|
||||||
DEVICE_CLASS_BLIND,
|
DEVICE_CLASS_BLIND,
|
||||||
@ -88,34 +89,38 @@ CoverClosedTrigger = cover_ns.class_(
|
|||||||
|
|
||||||
CONF_ON_CLOSED = "on_closed"
|
CONF_ON_CLOSED = "on_closed"
|
||||||
|
|
||||||
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
COVER_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(Cover),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
|
.extend(
|
||||||
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
|
{
|
||||||
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
|
cv.GenerateID(): cv.declare_id(Cover),
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
|
||||||
),
|
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
|
||||||
cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
|
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
|
||||||
{
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
|
||||||
),
|
{
|
||||||
}
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -132,6 +137,10 @@ async def setup_cover_core_(var, config):
|
|||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "ct_clamp_sensor.h"
|
#include "ct_clamp_sensor.h"
|
||||||
|
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include <cinttypes>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@ -37,8 +38,8 @@ void CTClampSensor::update() {
|
|||||||
float rms_ac = 0;
|
float rms_ac = 0;
|
||||||
if (rms_ac_squared > 0)
|
if (rms_ac_squared > 0)
|
||||||
rms_ac = std::sqrt(rms_ac_squared);
|
rms_ac = std::sqrt(rms_ac_squared);
|
||||||
ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %d different samples (%d SPS)", this->name_.c_str(), rms_ac,
|
ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %" PRIu32 " different samples (%" PRIu32 " SPS)",
|
||||||
this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
|
this->name_.c_str(), rms_ac, this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
|
||||||
this->publish_state(rms_ac);
|
this->publish_state(rms_ac);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt, time
|
from esphome.components import mqtt, web_server, time
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_ON_TIME,
|
CONF_ON_TIME,
|
||||||
@ -11,6 +11,7 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_DATE,
|
CONF_DATE,
|
||||||
CONF_DATETIME,
|
CONF_DATETIME,
|
||||||
CONF_TIME,
|
CONF_TIME,
|
||||||
@ -63,16 +64,20 @@ DATETIME_MODES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
_DATETIME_SCHEMA = cv.Schema(
|
_DATETIME_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
{
|
.extend(
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
|
{
|
||||||
}
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger),
|
||||||
}
|
}
|
||||||
).extend(cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA))
|
),
|
||||||
|
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def date_schema(class_: MockObjClass) -> cv.Schema:
|
def date_schema(class_: MockObjClass) -> cv.Schema:
|
||||||
@ -128,6 +133,9 @@ async def setup_datetime_core_(var, config):
|
|||||||
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
|
||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
for conf in config.get(CONF_ON_VALUE, []):
|
for conf in config.get(CONF_ON_VALUE, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
|
await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf)
|
||||||
|
@ -12,7 +12,7 @@ std::string DebugComponent::get_reset_reason_() { return lt_get_reboot_reason_na
|
|||||||
uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); }
|
uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); }
|
||||||
|
|
||||||
void DebugComponent::get_device_info_(std::string &device_info) {
|
void DebugComponent::get_device_info_(std::string &device_info) {
|
||||||
reset_reason = get_reset_reason_();
|
str::string reset_reason = get_reset_reason_();
|
||||||
ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
|
ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
|
||||||
ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
|
ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
|
||||||
ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
|
ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
|
||||||
|
@ -86,9 +86,14 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
|||||||
if (this->model_ == DHT_MODEL_DHT11) {
|
if (this->model_ == DHT_MODEL_DHT11) {
|
||||||
delayMicroseconds(18000);
|
delayMicroseconds(18000);
|
||||||
} else if (this->model_ == DHT_MODEL_SI7021) {
|
} else if (this->model_ == DHT_MODEL_SI7021) {
|
||||||
|
#ifdef USE_ESP8266
|
||||||
delayMicroseconds(500);
|
delayMicroseconds(500);
|
||||||
this->pin_->digital_write(true);
|
this->pin_->digital_write(true);
|
||||||
delayMicroseconds(40);
|
delayMicroseconds(40);
|
||||||
|
#else
|
||||||
|
delayMicroseconds(400);
|
||||||
|
this->pin_->digital_write(true);
|
||||||
|
#endif
|
||||||
} else if (this->model_ == DHT_MODEL_DHT22_TYPE2) {
|
} else if (this->model_ == DHT_MODEL_DHT22_TYPE2) {
|
||||||
delayMicroseconds(2000);
|
delayMicroseconds(2000);
|
||||||
} else if (this->model_ == DHT_MODEL_AM2120 || this->model_ == DHT_MODEL_AM2302) {
|
} else if (this->model_ == DHT_MODEL_AM2120 || this->model_ == DHT_MODEL_AM2302) {
|
||||||
|
@ -98,11 +98,15 @@ void EthernetComponent::setup() {
|
|||||||
.post_cb = nullptr,
|
.post_cb = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if USE_ESP_IDF && (ESP_IDF_VERSION_MAJOR >= 5)
|
||||||
|
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg);
|
||||||
|
#else
|
||||||
spi_device_handle_t spi_handle = nullptr;
|
spi_device_handle_t spi_handle = nullptr;
|
||||||
err = spi_bus_add_device(host, &devcfg, &spi_handle);
|
err = spi_bus_add_device(host, &devcfg, &spi_handle);
|
||||||
ESPHL_ERROR_CHECK(err, "SPI bus add device error");
|
ESPHL_ERROR_CHECK(err, "SPI bus add device error");
|
||||||
|
|
||||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
|
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle);
|
||||||
|
#endif
|
||||||
w5500_config.int_gpio_num = this->interrupt_pin_;
|
w5500_config.int_gpio_num = this->interrupt_pin_;
|
||||||
phy_config.phy_addr = this->phy_addr_spi_;
|
phy_config.phy_addr = this->phy_addr_spi_;
|
||||||
phy_config.reset_gpio_num = this->reset_pin_;
|
phy_config.reset_gpio_num = this->reset_pin_;
|
||||||
@ -406,7 +410,7 @@ void EthernetComponent::start_connect_() {
|
|||||||
global_eth_component->ipv6_count_ = 0;
|
global_eth_component->ipv6_count_ = 0;
|
||||||
#endif /* USE_NETWORK_IPV6 */
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
this->connect_begin_ = millis();
|
this->connect_begin_ = millis();
|
||||||
this->status_set_warning();
|
this->status_set_warning("waiting for IP configuration");
|
||||||
|
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
err = esp_netif_set_hostname(this->eth_netif_, App.get_name().c_str());
|
err = esp_netif_set_hostname(this->eth_netif_, App.get_name().c_str());
|
||||||
@ -572,11 +576,11 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
|
|||||||
/*
|
/*
|
||||||
* Bit 7 is `RMII Reference Clock Select`. Default is `0`.
|
* Bit 7 is `RMII Reference Clock Select`. Default is `0`.
|
||||||
* KSZ8081RNA:
|
* KSZ8081RNA:
|
||||||
* 0 - clock input to XI (Pin 8) is 25 MHz for RMII – 25 MHz clock mode.
|
* 0 - clock input to XI (Pin 8) is 25 MHz for RMII - 25 MHz clock mode.
|
||||||
* 1 - clock input to XI (Pin 8) is 50 MHz for RMII – 50 MHz clock mode.
|
* 1 - clock input to XI (Pin 8) is 50 MHz for RMII - 50 MHz clock mode.
|
||||||
* KSZ8081RND:
|
* KSZ8081RND:
|
||||||
* 0 - clock input to XI (Pin 8) is 50 MHz for RMII – 50 MHz clock mode.
|
* 0 - clock input to XI (Pin 8) is 50 MHz for RMII - 50 MHz clock mode.
|
||||||
* 1 - clock input to XI (Pin 8) is 25 MHz (driven clock only, not a crystal) for RMII – 25 MHz clock mode.
|
* 1 - clock input to XI (Pin 8) is 25 MHz (driven clock only, not a crystal) for RMII - 25 MHz clock mode.
|
||||||
*/
|
*/
|
||||||
if ((phy_control_2 & (1 << 7)) != (1 << 7)) {
|
if ((phy_control_2 & (1 << 7)) != (1 << 7)) {
|
||||||
phy_control_2 |= 1 << 7;
|
phy_control_2 |= 1 << 7;
|
||||||
@ -614,14 +618,14 @@ void EthernetComponent::rtl8201_set_rmii_mode_(esp_eth_mac_t *mac) {
|
|||||||
|
|
||||||
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode));
|
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode));
|
||||||
ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed");
|
ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed");
|
||||||
ESP_LOGV(TAG, "Hardware default RTL8201 RMII Mode Register is: 0x%04X", phy_rmii_mode);
|
ESP_LOGV(TAG, "Hardware default RTL8201 RMII Mode Register is: 0x%04" PRIX32, phy_rmii_mode);
|
||||||
|
|
||||||
err = mac->write_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, 0x1FFA);
|
err = mac->write_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, 0x1FFA);
|
||||||
ESPHL_ERROR_CHECK(err, "Setting Register 16 RMII Mode Setting failed");
|
ESPHL_ERROR_CHECK(err, "Setting Register 16 RMII Mode Setting failed");
|
||||||
|
|
||||||
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode));
|
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode));
|
||||||
ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed");
|
ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed");
|
||||||
ESP_LOGV(TAG, "Setting RTL8201 RMII Mode Register to: 0x%04X", phy_rmii_mode);
|
ESP_LOGV(TAG, "Setting RTL8201 RMII Mode Register to: 0x%04" PRIX32, phy_rmii_mode);
|
||||||
|
|
||||||
err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x0);
|
err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x0);
|
||||||
ESPHL_ERROR_CHECK(err, "Setting Page 0 failed");
|
ESPHL_ERROR_CHECK(err, "Setting Page 0 failed");
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "esp_eth.h"
|
#include "esp_eth.h"
|
||||||
#include "esp_eth_mac.h"
|
#include "esp_eth_mac.h"
|
||||||
#include "esp_netif.h"
|
#include "esp_netif.h"
|
||||||
|
#include "esp_mac.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ethernet {
|
namespace ethernet {
|
||||||
|
@ -2,10 +2,11 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import maybe_simple_id
|
from esphome.automation import maybe_simple_id
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_OSCILLATING,
|
CONF_OSCILLATING,
|
||||||
CONF_OSCILLATION_COMMAND_TOPIC,
|
CONF_OSCILLATION_COMMAND_TOPIC,
|
||||||
CONF_OSCILLATION_STATE_TOPIC,
|
CONF_OSCILLATION_STATE_TOPIC,
|
||||||
@ -79,67 +80,75 @@ FanPresetSetTrigger = fan_ns.class_(
|
|||||||
FanIsOnCondition = fan_ns.class_("FanIsOnCondition", automation.Condition.template())
|
FanIsOnCondition = fan_ns.class_("FanIsOnCondition", automation.Condition.template())
|
||||||
FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.template())
|
FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.template())
|
||||||
|
|
||||||
FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
FAN_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(Fan),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
.extend(
|
||||||
RESTORE_MODES, upper=True, space="_"
|
{
|
||||||
),
|
cv.GenerateID(): cv.declare_id(Fan),
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
|
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
||||||
cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All(
|
RESTORE_MODES, upper=True, space="_"
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
),
|
||||||
),
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
|
||||||
cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SPEED_LEVEL_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SPEED_LEVEL_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_SPEED_LEVEL_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_SPEED_LEVEL_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.publish_topic
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SPEED_COMMAND_TOPIC): cv.All(
|
cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.publish_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
cv.Optional(CONF_SPEED_COMMAND_TOPIC): cv.All(
|
||||||
{
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanStateTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanStateTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanTurnOnTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanTurnOnTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanTurnOffTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_DIRECTION_SET): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanTurnOffTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanDirectionSetTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_DIRECTION_SET): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_OSCILLATING_SET): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
{
|
FanDirectionSetTrigger
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanOscillatingSetTrigger),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_SPEED_SET): automation.validate_automation(
|
cv.Optional(CONF_ON_OSCILLATING_SET): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanSpeedSetTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
}
|
FanOscillatingSetTrigger
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_PRESET_SET): automation.validate_automation(
|
}
|
||||||
{
|
),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanPresetSetTrigger),
|
cv.Optional(CONF_ON_SPEED_SET): automation.validate_automation(
|
||||||
}
|
{
|
||||||
),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanSpeedSetTrigger),
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_PRESET_SET): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FanPresetSetTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_PRESET_MODES_SCHEMA = cv.All(
|
_PRESET_MODES_SCHEMA = cv.All(
|
||||||
@ -209,6 +218,10 @@ async def setup_fan_core_(var, config):
|
|||||||
if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None:
|
if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None:
|
||||||
cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic))
|
cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic))
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_STATE, []):
|
for conf in config.get(CONF_ON_STATE, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [(Fan.operator("ptr"), "x")], conf)
|
await automation.build_automation(trigger, [(Fan.operator("ptr"), "x")], conf)
|
||||||
|
@ -244,7 +244,7 @@ void FeedbackCover::loop() {
|
|||||||
|
|
||||||
// update current position at requested interval, regardless of who started the movement
|
// update current position at requested interval, regardless of who started the movement
|
||||||
// so that we also update UI if there was an external movement
|
// so that we also update UI if there was an external movement
|
||||||
// don´t save intermediate positions
|
// don't save intermediate positions
|
||||||
if (now - this->last_publish_time_ > this->update_interval_) {
|
if (now - this->last_publish_time_ > this->update_interval_) {
|
||||||
this->publish_state(false);
|
this->publish_state(false);
|
||||||
this->last_publish_time_ = now;
|
this->last_publish_time_ = now;
|
||||||
@ -274,7 +274,7 @@ void FeedbackCover::control(const CoverCall &call) {
|
|||||||
if (pos == this->position) {
|
if (pos == this->position) {
|
||||||
// already at target,
|
// already at target,
|
||||||
|
|
||||||
// for covers with built in end stop, if we don´t have sensors we should send the command again
|
// for covers with built in end stop, if we don't have sensors we should send the command again
|
||||||
// to make sure the assumed state is not wrong
|
// to make sure the assumed state is not wrong
|
||||||
if (this->has_built_in_endstop_ && ((pos == COVER_OPEN
|
if (this->has_built_in_endstop_ && ((pos == COVER_OPEN
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
@ -377,7 +377,7 @@ uint8_t FingerprintGrowComponent::transfer_(std::vector<uint8_t> *p_data_buffer)
|
|||||||
this->write((uint8_t) (wire_length >> 8));
|
this->write((uint8_t) (wire_length >> 8));
|
||||||
this->write((uint8_t) (wire_length & 0xFF));
|
this->write((uint8_t) (wire_length & 0xFF));
|
||||||
|
|
||||||
uint16_t sum = ((wire_length) >> 8) + ((wire_length) &0xFF) + COMMAND;
|
uint16_t sum = (wire_length >> 8) + (wire_length & 0xFF) + COMMAND;
|
||||||
for (auto data : *p_data_buffer) {
|
for (auto data : *p_data_buffer) {
|
||||||
this->write(data);
|
this->write(data);
|
||||||
sum += data;
|
sum += data;
|
||||||
@ -541,34 +541,34 @@ void FingerprintGrowComponent::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Sensor Power Pin: %s",
|
ESP_LOGCONFIG(TAG, " Sensor Power Pin: %s",
|
||||||
this->has_power_pin_ ? this->sensor_power_pin_->dump_summary().c_str() : "None");
|
this->has_power_pin_ ? this->sensor_power_pin_->dump_summary().c_str() : "None");
|
||||||
if (this->idle_period_to_sleep_ms_ < UINT32_MAX) {
|
if (this->idle_period_to_sleep_ms_ < UINT32_MAX) {
|
||||||
ESP_LOGCONFIG(TAG, " Idle Period to Sleep: %u ms", this->idle_period_to_sleep_ms_);
|
ESP_LOGCONFIG(TAG, " Idle Period to Sleep: %" PRIu32 " ms", this->idle_period_to_sleep_ms_);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGCONFIG(TAG, " Idle Period to Sleep: Never");
|
ESP_LOGCONFIG(TAG, " Idle Period to Sleep: Never");
|
||||||
}
|
}
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
if (this->fingerprint_count_sensor_) {
|
if (this->fingerprint_count_sensor_) {
|
||||||
LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
|
LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->fingerprint_count_sensor_->get_state());
|
ESP_LOGCONFIG(TAG, " Current Value: %u", (uint16_t) this->fingerprint_count_sensor_->get_state());
|
||||||
}
|
}
|
||||||
if (this->status_sensor_) {
|
if (this->status_sensor_) {
|
||||||
LOG_SENSOR(" ", "Status", this->status_sensor_);
|
LOG_SENSOR(" ", "Status", this->status_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->status_sensor_->get_state());
|
ESP_LOGCONFIG(TAG, " Current Value: %u", (uint8_t) this->status_sensor_->get_state());
|
||||||
}
|
}
|
||||||
if (this->capacity_sensor_) {
|
if (this->capacity_sensor_) {
|
||||||
LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
|
LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->capacity_sensor_->get_state());
|
ESP_LOGCONFIG(TAG, " Current Value: %u", (uint16_t) this->capacity_sensor_->get_state());
|
||||||
}
|
}
|
||||||
if (this->security_level_sensor_) {
|
if (this->security_level_sensor_) {
|
||||||
LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
|
LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->security_level_sensor_->get_state());
|
ESP_LOGCONFIG(TAG, " Current Value: %u", (uint8_t) this->security_level_sensor_->get_state());
|
||||||
}
|
}
|
||||||
if (this->last_finger_id_sensor_) {
|
if (this->last_finger_id_sensor_) {
|
||||||
LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
|
LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_finger_id_sensor_->get_state());
|
ESP_LOGCONFIG(TAG, " Current Value: %" PRIu32, (uint32_t) this->last_finger_id_sensor_->get_state());
|
||||||
}
|
}
|
||||||
if (this->last_confidence_sensor_) {
|
if (this->last_confidence_sensor_) {
|
||||||
LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
|
LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_confidence_sensor_->get_state());
|
ESP_LOGCONFIG(TAG, " Current Value: %" PRIu32, (uint32_t) this->last_confidence_sensor_->get_state());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace he60r {
|
namespace he60r {
|
||||||
|
|
||||||
@ -124,10 +126,10 @@ void HE60rCover::process_rx_(uint8_t data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HE60rCover::update_() {
|
void HE60rCover::update_() {
|
||||||
if (toggles_needed_ != 0) {
|
if (this->toggles_needed_ != 0) {
|
||||||
if ((this->counter_++ & 0x3) == 0) {
|
if ((this->counter_++ & 0x3) == 0) {
|
||||||
toggles_needed_--;
|
this->toggles_needed_--;
|
||||||
ESP_LOGD(TAG, "Writing byte 0x30, still needed=%d", toggles_needed_);
|
ESP_LOGD(TAG, "Writing byte 0x30, still needed=%" PRIu32, this->toggles_needed_);
|
||||||
this->write_byte(TOGGLE_BYTE);
|
this->write_byte(TOGGLE_BYTE);
|
||||||
} else {
|
} else {
|
||||||
this->write_byte(QUERY_BYTE);
|
this->write_byte(QUERY_BYTE);
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace htu31d {
|
namespace htu31d {
|
||||||
|
|
||||||
@ -204,7 +206,7 @@ uint32_t HTU31DComponent::read_serial_num_() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Found serial: 0x%X", serial);
|
ESP_LOGD(TAG, "Found serial: 0x%" PRIX32, serial);
|
||||||
|
|
||||||
return serial;
|
return serial;
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,13 @@ from esphome.const import (
|
|||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
STATE_CLASS_TOTAL_INCREASING,
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
|
UNIT_MILLIMETER,
|
||||||
ICON_THERMOMETER,
|
ICON_THERMOMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import RGModel, RG15Resolution, HydreonRGxxComponent
|
from . import RGModel, RG15Resolution, HydreonRGxxComponent
|
||||||
|
|
||||||
UNIT_INTENSITY = "intensity"
|
UNIT_INTENSITY = "intensity"
|
||||||
UNIT_MILLIMETERS = "mm"
|
|
||||||
UNIT_MILLIMETERS_PER_HOUR = "mm/h"
|
UNIT_MILLIMETERS_PER_HOUR = "mm/h"
|
||||||
|
|
||||||
CONF_ACC = "acc"
|
CONF_ACC = "acc"
|
||||||
@ -85,19 +85,19 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
),
|
),
|
||||||
cv.Optional(CONF_RESOLUTION): cv.enum(RG15_RESOLUTION, upper=False),
|
cv.Optional(CONF_RESOLUTION): cv.enum(RG15_RESOLUTION, upper=False),
|
||||||
cv.Optional(CONF_ACC): sensor.sensor_schema(
|
cv.Optional(CONF_ACC): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_MILLIMETERS,
|
unit_of_measurement=UNIT_MILLIMETER,
|
||||||
accuracy_decimals=2,
|
accuracy_decimals=2,
|
||||||
device_class=DEVICE_CLASS_PRECIPITATION,
|
device_class=DEVICE_CLASS_PRECIPITATION,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_EVENT_ACC): sensor.sensor_schema(
|
cv.Optional(CONF_EVENT_ACC): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_MILLIMETERS,
|
unit_of_measurement=UNIT_MILLIMETER,
|
||||||
accuracy_decimals=2,
|
accuracy_decimals=2,
|
||||||
device_class=DEVICE_CLASS_PRECIPITATION,
|
device_class=DEVICE_CLASS_PRECIPITATION,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_TOTAL_ACC): sensor.sensor_schema(
|
cv.Optional(CONF_TOTAL_ACC): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_MILLIMETERS,
|
unit_of_measurement=UNIT_MILLIMETER,
|
||||||
accuracy_decimals=2,
|
accuracy_decimals=2,
|
||||||
device_class=DEVICE_CLASS_PRECIPITATION,
|
device_class=DEVICE_CLASS_PRECIPITATION,
|
||||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
@ -9,6 +9,10 @@ namespace i2s_audio {
|
|||||||
|
|
||||||
static const char *const TAG = "i2s_audio";
|
static const char *const TAG = "i2s_audio";
|
||||||
|
|
||||||
|
#if defined(USE_ESP_IDF) && (ESP_IDF_VERSION_MAJOR >= 5)
|
||||||
|
static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this away :(
|
||||||
|
#endif
|
||||||
|
|
||||||
void I2SAudioComponent::setup() {
|
void I2SAudioComponent::setup() {
|
||||||
static i2s_port_t next_port_num = I2S_NUM_0;
|
static i2s_port_t next_port_num = I2S_NUM_0;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ void I2SAudioMicrophone::start_() {
|
|||||||
.use_apll = this->use_apll_,
|
.use_apll = this->use_apll_,
|
||||||
.tx_desc_auto_clear = false,
|
.tx_desc_auto_clear = false,
|
||||||
.fixed_mclk = 0,
|
.fixed_mclk = 0,
|
||||||
.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
|
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
|
||||||
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
|
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,10 +19,27 @@ void I2SAudioSpeaker::setup() {
|
|||||||
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker...");
|
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker...");
|
||||||
|
|
||||||
this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent));
|
this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent));
|
||||||
|
if (this->buffer_queue_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create buffer queue");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent));
|
this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent));
|
||||||
|
if (this->event_queue_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create event queue");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2SAudioSpeaker::start() { this->state_ = speaker::STATE_STARTING; }
|
void I2SAudioSpeaker::start() {
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Cannot start audio, speaker failed to setup");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->state_ = speaker::STATE_STARTING;
|
||||||
|
}
|
||||||
void I2SAudioSpeaker::start_() {
|
void I2SAudioSpeaker::start_() {
|
||||||
if (!this->parent_->try_lock()) {
|
if (!this->parent_->try_lock()) {
|
||||||
return; // Waiting for another i2s component to return lock
|
return; // Waiting for another i2s component to return lock
|
||||||
@ -51,7 +68,7 @@ void I2SAudioSpeaker::player_task(void *params) {
|
|||||||
.use_apll = false,
|
.use_apll = false,
|
||||||
.tx_desc_auto_clear = true,
|
.tx_desc_auto_clear = true,
|
||||||
.fixed_mclk = I2S_PIN_NO_CHANGE,
|
.fixed_mclk = I2S_PIN_NO_CHANGE,
|
||||||
.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
|
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
|
||||||
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
|
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
|
||||||
};
|
};
|
||||||
#if SOC_I2S_SUPPORTS_DAC
|
#if SOC_I2S_SUPPORTS_DAC
|
||||||
@ -141,6 +158,8 @@ void I2SAudioSpeaker::player_task(void *params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void I2SAudioSpeaker::stop() {
|
void I2SAudioSpeaker::stop() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return;
|
||||||
if (this->state_ == speaker::STATE_STOPPED)
|
if (this->state_ == speaker::STATE_STOPPED)
|
||||||
return;
|
return;
|
||||||
if (this->state_ == speaker::STATE_STARTING) {
|
if (this->state_ == speaker::STATE_STARTING) {
|
||||||
@ -200,6 +219,10 @@ void I2SAudioSpeaker::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length) {
|
size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length) {
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
|
if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
|
||||||
this->start();
|
this->start();
|
||||||
}
|
}
|
||||||
|
@ -483,7 +483,7 @@ bool INA2XX::read_power_w_(float &power_out) {
|
|||||||
uint64_t power_reading{0};
|
uint64_t power_reading{0};
|
||||||
auto ret = this->read_unsigned_((uint8_t) RegisterMap::REG_POWER, 3, power_reading);
|
auto ret = this->read_unsigned_((uint8_t) RegisterMap::REG_POWER, 3, power_reading);
|
||||||
|
|
||||||
ESP_LOGV(TAG, "read_power_w_ ret=%s, reading_lsb=%d", OKFAILED(ret), (uint32_t) power_reading);
|
ESP_LOGV(TAG, "read_power_w_ ret=%s, reading_lsb=%" PRIu32, OKFAILED(ret), (uint32_t) power_reading);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
power_out = this->cfg_.power_coeff * this->current_lsb_ * (float) power_reading;
|
power_out = this->cfg_.power_coeff * this->current_lsb_ * (float) power_reading;
|
||||||
}
|
}
|
||||||
@ -503,8 +503,8 @@ bool INA2XX::read_energy_(double &joules_out, double &watt_hours_out) {
|
|||||||
uint64_t previous_energy = this->energy_overflows_count_ * (((uint64_t) 1) << 40);
|
uint64_t previous_energy = this->energy_overflows_count_ * (((uint64_t) 1) << 40);
|
||||||
auto ret = this->read_unsigned_((uint8_t) RegisterMap::REG_ENERGY, 5, joules_reading);
|
auto ret = this->read_unsigned_((uint8_t) RegisterMap::REG_ENERGY, 5, joules_reading);
|
||||||
|
|
||||||
ESP_LOGV(TAG, "read_energy_j_ ret=%s, reading_lsb=0x%" PRIX64 ", current_lsb=%f, overflow_cnt=%d", OKFAILED(ret),
|
ESP_LOGV(TAG, "read_energy_j_ ret=%s, reading_lsb=0x%" PRIX64 ", current_lsb=%f, overflow_cnt=%" PRIu32,
|
||||||
joules_reading, this->current_lsb_, this->energy_overflows_count_);
|
OKFAILED(ret), joules_reading, this->current_lsb_, this->energy_overflows_count_);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
joules_out = this->cfg_.energy_coeff * this->current_lsb_ * (double) joules_reading + (double) previous_energy;
|
joules_out = this->cfg_.energy_coeff * this->current_lsb_ * (double) joules_reading + (double) previous_energy;
|
||||||
watt_hours_out = joules_out / 3600.0;
|
watt_hours_out = joules_out / 3600.0;
|
||||||
@ -528,7 +528,7 @@ bool INA2XX::read_charge_(double &coulombs_out, double &_hours_out) {
|
|||||||
auto ret = this->read_unsigned_((uint8_t) RegisterMap::REG_CHARGE, 5, raw);
|
auto ret = this->read_unsigned_((uint8_t) RegisterMap::REG_CHARGE, 5, raw);
|
||||||
coulombs_reading = this->two_complement_(raw, 40);
|
coulombs_reading = this->two_complement_(raw, 40);
|
||||||
|
|
||||||
ESP_LOGV(TAG, "read_charge_c_ ret=%d, curr_charge=%f + 39-bit overflow_cnt=%d", ret, coulombs_reading,
|
ESP_LOGV(TAG, "read_charge_c_ ret=%d, curr_charge=%f + 39-bit overflow_cnt=%" PRIu32, ret, coulombs_reading,
|
||||||
this->charge_overflows_count_);
|
this->charge_overflows_count_);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
coulombs_out = this->current_lsb_ * (double) coulombs_reading + (double) previous_charge;
|
coulombs_out = this->current_lsb_ * (double) coulombs_reading + (double) previous_charge;
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#include <cinttypes>
|
|
||||||
|
|
||||||
// Very basic support for JSN_SR04T V3.0 distance sensor in mode 2
|
// Very basic support for JSN_SR04T V3.0 distance sensor in mode 2
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@ -38,7 +36,7 @@ void Jsnsr04tComponent::check_buffer_() {
|
|||||||
uint16_t distance = encode_uint16(this->buffer_[1], this->buffer_[2]);
|
uint16_t distance = encode_uint16(this->buffer_[1], this->buffer_[2]);
|
||||||
if (distance > 250) {
|
if (distance > 250) {
|
||||||
float meters = distance / 1000.0f;
|
float meters = distance / 1000.0f;
|
||||||
ESP_LOGV(TAG, "Distance from sensor: %" PRIu32 "mm, %.3fm", distance, meters);
|
ESP_LOGV(TAG, "Distance from sensor: %umm, %.3fm", distance, meters);
|
||||||
this->publish_state(meters);
|
this->publish_state(meters);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
import esphome.automation as auto
|
import esphome.automation as auto
|
||||||
from esphome.components import mqtt, power_supply
|
from esphome.components import mqtt, power_supply, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_COLOR_CORRECT,
|
CONF_COLOR_CORRECT,
|
||||||
CONF_DEFAULT_TRANSITION_LENGTH,
|
CONF_DEFAULT_TRANSITION_LENGTH,
|
||||||
@ -10,6 +10,7 @@ from esphome.const import (
|
|||||||
CONF_GAMMA_CORRECT,
|
CONF_GAMMA_CORRECT,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_POWER_SUPPLY,
|
CONF_POWER_SUPPLY,
|
||||||
CONF_RESTORE_MODE,
|
CONF_RESTORE_MODE,
|
||||||
CONF_ON_TURN_OFF,
|
CONF_ON_TURN_OFF,
|
||||||
@ -56,29 +57,35 @@ RESTORE_MODES = {
|
|||||||
"RESTORE_AND_ON": LightRestoreMode.LIGHT_RESTORE_AND_ON,
|
"RESTORE_AND_ON": LightRestoreMode.LIGHT_RESTORE_AND_ON,
|
||||||
}
|
}
|
||||||
|
|
||||||
LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
LIGHT_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(LightState),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent),
|
.extend(
|
||||||
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
{
|
||||||
RESTORE_MODES, upper=True, space="_"
|
cv.GenerateID(): cv.declare_id(LightState),
|
||||||
),
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||||
cv.Optional(CONF_ON_TURN_ON): auto.validate_automation(
|
mqtt.MQTTJSONLightComponent
|
||||||
{
|
),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOnTrigger),
|
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
||||||
}
|
RESTORE_MODES, upper=True, space="_"
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_TURN_OFF): auto.validate_automation(
|
cv.Optional(CONF_ON_TURN_ON): auto.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOffTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOnTrigger),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_STATE): auto.validate_automation(
|
cv.Optional(CONF_ON_TURN_OFF): auto.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightStateTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOffTrigger),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_STATE): auto.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightStateTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend(
|
BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend(
|
||||||
@ -173,6 +180,10 @@ async def setup_light_core_(light_var, output_var, config):
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, light_var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, light_var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, light_var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_light(output_var, config):
|
async def register_light(output_var, config):
|
||||||
light_var = cg.new_Pvariable(config[CONF_ID], output_var)
|
light_var = cg.new_Pvariable(config[CONF_ID], output_var)
|
||||||
|
@ -2,13 +2,14 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition, maybe_simple_id
|
from esphome.automation import Condition, maybe_simple_id
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_ON_LOCK,
|
CONF_ON_LOCK,
|
||||||
CONF_ON_UNLOCK,
|
CONF_ON_UNLOCK,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
@ -30,20 +31,24 @@ LockCondition = lock_ns.class_("LockCondition", Condition)
|
|||||||
LockLockTrigger = lock_ns.class_("LockLockTrigger", automation.Trigger.template())
|
LockLockTrigger = lock_ns.class_("LockLockTrigger", automation.Trigger.template())
|
||||||
LockUnlockTrigger = lock_ns.class_("LockUnlockTrigger", automation.Trigger.template())
|
LockUnlockTrigger = lock_ns.class_("LockUnlockTrigger", automation.Trigger.template())
|
||||||
|
|
||||||
LOCK_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
LOCK_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTLockComponent),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.Optional(CONF_ON_LOCK): automation.validate_automation(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockLockTrigger),
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTLockComponent),
|
||||||
}
|
cv.Optional(CONF_ON_LOCK): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_UNLOCK): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockLockTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockUnlockTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_UNLOCK): automation.validate_automation(
|
||||||
),
|
{
|
||||||
}
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LockUnlockTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -61,6 +66,10 @@ async def setup_lock_core_(var, config):
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_lock(var, config):
|
async def register_lock(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -2,14 +2,15 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_AMBIENT_LIGHT,
|
||||||
CONF_GAIN,
|
CONF_GAIN,
|
||||||
|
CONF_ID,
|
||||||
CONF_LIGHT,
|
CONF_LIGHT,
|
||||||
CONF_RESOLUTION,
|
CONF_RESOLUTION,
|
||||||
UNIT_LUX,
|
|
||||||
ICON_BRIGHTNESS_5,
|
|
||||||
DEVICE_CLASS_EMPTY,
|
DEVICE_CLASS_EMPTY,
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
ICON_BRIGHTNESS_5,
|
||||||
|
UNIT_LUX,
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@sjtrny"]
|
CODEOWNERS = ["@sjtrny"]
|
||||||
@ -21,7 +22,6 @@ LTR390Component = ltr390_ns.class_(
|
|||||||
"LTR390Component", cg.PollingComponent, i2c.I2CDevice
|
"LTR390Component", cg.PollingComponent, i2c.I2CDevice
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_AMBIENT_LIGHT = "ambient_light"
|
|
||||||
CONF_UV_INDEX = "uv_index"
|
CONF_UV_INDEX = "uv_index"
|
||||||
CONF_UV = "uv"
|
CONF_UV = "uv"
|
||||||
CONF_WINDOW_CORRECTION_FACTOR = "window_correction_factor"
|
CONF_WINDOW_CORRECTION_FACTOR = "window_correction_factor"
|
||||||
|
1
esphome/components/ltr_als_ps/__init__.py
Normal file
1
esphome/components/ltr_als_ps/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
CODEOWNERS = ["@latonita"]
|
519
esphome/components/ltr_als_ps/ltr_als_ps.cpp
Normal file
519
esphome/components/ltr_als_ps/ltr_als_ps.cpp
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
#include "ltr_als_ps.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
using esphome::i2c::ErrorCode;
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ltr_als_ps {
|
||||||
|
|
||||||
|
static const char *const TAG = "ltr_als_ps";
|
||||||
|
|
||||||
|
static const uint8_t MAX_TRIES = 5;
|
||||||
|
|
||||||
|
template<typename T, size_t size> T get_next(const T (&array)[size], const T val) {
|
||||||
|
size_t i = 0;
|
||||||
|
size_t idx = -1;
|
||||||
|
while (idx == -1 && i < size) {
|
||||||
|
if (array[i] == val) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (idx == -1 || i + 1 >= size)
|
||||||
|
return val;
|
||||||
|
return array[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t size> T get_prev(const T (&array)[size], const T val) {
|
||||||
|
size_t i = size - 1;
|
||||||
|
size_t idx = -1;
|
||||||
|
while (idx == -1 && i > 0) {
|
||||||
|
if (array[i] == val) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (idx == -1 || i == 0)
|
||||||
|
return val;
|
||||||
|
return array[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t get_itime_ms(IntegrationTime time) {
|
||||||
|
static const uint16_t ALS_INT_TIME[8] = {100, 50, 200, 400, 150, 250, 300, 350};
|
||||||
|
return ALS_INT_TIME[time & 0b111];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t get_meas_time_ms(MeasurementRepeatRate rate) {
|
||||||
|
static const uint16_t ALS_MEAS_RATE[8] = {50, 100, 200, 500, 1000, 2000, 2000, 2000};
|
||||||
|
return ALS_MEAS_RATE[rate & 0b111];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float get_gain_coeff(AlsGain gain) {
|
||||||
|
static const float ALS_GAIN[8] = {1, 2, 4, 8, 0, 0, 48, 96};
|
||||||
|
return ALS_GAIN[gain & 0b111];
|
||||||
|
}
|
||||||
|
|
||||||
|
static float get_ps_gain_coeff(PsGain gain) {
|
||||||
|
static const float PS_GAIN[4] = {16, 0, 32, 64};
|
||||||
|
return PS_GAIN[gain & 0b11];
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up LTR-303/329/55x/659");
|
||||||
|
// As per datasheet we need to wait at least 100ms after power on to get ALS chip responsive
|
||||||
|
this->set_timeout(100, [this]() { this->state_ = State::DELAYED_SETUP; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::dump_config() {
|
||||||
|
auto get_device_type = [](LtrType typ) {
|
||||||
|
switch (typ) {
|
||||||
|
case LtrType::LTR_TYPE_ALS_ONLY:
|
||||||
|
return "ALS only";
|
||||||
|
case LtrType::LTR_TYPE_PS_ONLY:
|
||||||
|
return "PS only";
|
||||||
|
case LtrType::LTR_TYPE_ALS_AND_PS:
|
||||||
|
return "ALS + PS";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
ESP_LOGCONFIG(TAG, " Device type: %s", get_device_type(this->ltr_type_));
|
||||||
|
if (this->is_als_()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Automatic mode: %s", ONOFF(this->automatic_mode_enabled_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Gain: %.0fx", get_gain_coeff(this->gain_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Integration time: %d ms", get_itime_ms(this->integration_time_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Measurement repeat rate: %d ms", get_meas_time_ms(this->repeat_rate_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Glass attenuation factor: %f", this->glass_attenuation_factor_);
|
||||||
|
LOG_SENSOR(" ", "ALS calculated lux", this->ambient_light_sensor_);
|
||||||
|
LOG_SENSOR(" ", "CH1 Infrared counts", this->infrared_counts_sensor_);
|
||||||
|
LOG_SENSOR(" ", "CH0 Visible+IR counts", this->full_spectrum_counts_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Actual gain", this->actual_gain_sensor_);
|
||||||
|
}
|
||||||
|
if (this->is_ps_()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Proximity gain: %.0fx", get_ps_gain_coeff(this->ps_gain_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Proximity cooldown time: %d s", this->ps_cooldown_time_s_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Proximity high threshold: %d", this->ps_threshold_high_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Proximity low threshold: %d", this->ps_threshold_low_);
|
||||||
|
LOG_SENSOR(" ", "Proximity counts", this->proximity_counts_sensor_);
|
||||||
|
}
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Communication with I2C LTR-303/329/55x/659 failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::update() {
|
||||||
|
ESP_LOGV(TAG, "Updating");
|
||||||
|
if (this->is_ready() && this->state_ == State::IDLE) {
|
||||||
|
ESP_LOGV(TAG, "Initiating new data collection");
|
||||||
|
|
||||||
|
this->state_ = this->automatic_mode_enabled_ ? State::COLLECTING_DATA_AUTO : State::WAITING_FOR_DATA;
|
||||||
|
|
||||||
|
this->als_readings_.ch0 = 0;
|
||||||
|
this->als_readings_.ch1 = 0;
|
||||||
|
this->als_readings_.gain = this->gain_;
|
||||||
|
this->als_readings_.integration_time = this->integration_time_;
|
||||||
|
this->als_readings_.lux = 0;
|
||||||
|
this->als_readings_.number_of_adjustments = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ESP_LOGV(TAG, "Component not ready yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::loop() {
|
||||||
|
ErrorCode err = i2c::ERROR_OK;
|
||||||
|
static uint8_t tries{0};
|
||||||
|
|
||||||
|
switch (this->state_) {
|
||||||
|
case State::DELAYED_SETUP:
|
||||||
|
err = this->write(nullptr, 0);
|
||||||
|
if (err != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGV(TAG, "i2c connection failed");
|
||||||
|
this->mark_failed();
|
||||||
|
}
|
||||||
|
this->configure_reset_();
|
||||||
|
if (this->is_als_()) {
|
||||||
|
this->configure_als_();
|
||||||
|
this->configure_integration_time_(this->integration_time_);
|
||||||
|
}
|
||||||
|
if (this->is_ps_()) {
|
||||||
|
this->configure_ps_();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->state_ = State::IDLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::IDLE:
|
||||||
|
if (this->is_ps_()) {
|
||||||
|
check_and_trigger_ps_();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::WAITING_FOR_DATA:
|
||||||
|
if (this->is_als_data_ready_(this->als_readings_) == DataAvail::DATA_OK) {
|
||||||
|
tries = 0;
|
||||||
|
ESP_LOGV(TAG, "Reading sensor data having gain = %.0fx, time = %d ms", get_gain_coeff(this->als_readings_.gain),
|
||||||
|
get_itime_ms(this->als_readings_.integration_time));
|
||||||
|
this->read_sensor_data_(this->als_readings_);
|
||||||
|
this->state_ = State::DATA_COLLECTED;
|
||||||
|
this->apply_lux_calculation_(this->als_readings_);
|
||||||
|
} else if (tries >= MAX_TRIES) {
|
||||||
|
ESP_LOGW(TAG, "Can't get data after several tries.");
|
||||||
|
tries = 0;
|
||||||
|
this->status_set_warning();
|
||||||
|
this->state_ = State::IDLE;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
tries++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::COLLECTING_DATA_AUTO:
|
||||||
|
case State::DATA_COLLECTED:
|
||||||
|
// first measurement in auto mode (COLLECTING_DATA_AUTO state) require device reconfiguration
|
||||||
|
if (this->state_ == State::COLLECTING_DATA_AUTO || this->are_adjustments_required_(this->als_readings_)) {
|
||||||
|
this->state_ = State::ADJUSTMENT_IN_PROGRESS;
|
||||||
|
ESP_LOGD(TAG, "Reconfiguring sensitivity: gain = %.0fx, time = %d ms", get_gain_coeff(this->als_readings_.gain),
|
||||||
|
get_itime_ms(this->als_readings_.integration_time));
|
||||||
|
this->configure_integration_time_(this->als_readings_.integration_time);
|
||||||
|
this->configure_gain_(this->als_readings_.gain);
|
||||||
|
// if sensitivity adjustment needed - need to wait for first data samples after setting new parameters
|
||||||
|
this->set_timeout(2 * get_meas_time_ms(this->repeat_rate_),
|
||||||
|
[this]() { this->state_ = State::WAITING_FOR_DATA; });
|
||||||
|
} else {
|
||||||
|
this->state_ = State::READY_TO_PUBLISH;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::ADJUSTMENT_IN_PROGRESS:
|
||||||
|
// nothing to be done, just waiting for the timeout
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::READY_TO_PUBLISH:
|
||||||
|
this->publish_data_part_1_(this->als_readings_);
|
||||||
|
this->state_ = State::KEEP_PUBLISHING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case State::KEEP_PUBLISHING:
|
||||||
|
this->publish_data_part_2_(this->als_readings_);
|
||||||
|
this->status_clear_warning();
|
||||||
|
this->state_ = State::IDLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::check_and_trigger_ps_() {
|
||||||
|
static uint32_t last_high_trigger_time{0};
|
||||||
|
static uint32_t last_low_trigger_time{0};
|
||||||
|
uint16_t ps_data = this->read_ps_data_();
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
|
if (ps_data != this->ps_readings_) {
|
||||||
|
this->ps_readings_ = ps_data;
|
||||||
|
// Higher values - object is closer to sensor
|
||||||
|
if (ps_data > this->ps_threshold_high_ && now - last_high_trigger_time >= this->ps_cooldown_time_s_ * 1000) {
|
||||||
|
last_high_trigger_time = now;
|
||||||
|
ESP_LOGV(TAG, "Proximity high threshold triggered. Value = %d, Trigger level = %d", ps_data,
|
||||||
|
this->ps_threshold_high_);
|
||||||
|
this->on_ps_high_trigger_callback_.call();
|
||||||
|
} else if (ps_data < this->ps_threshold_low_ && now - last_low_trigger_time >= this->ps_cooldown_time_s_ * 1000) {
|
||||||
|
last_low_trigger_time = now;
|
||||||
|
ESP_LOGV(TAG, "Proximity low threshold triggered. Value = %d, Trigger level = %d", ps_data,
|
||||||
|
this->ps_threshold_low_);
|
||||||
|
this->on_ps_low_trigger_callback_.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LTRAlsPsComponent::check_part_number_() {
|
||||||
|
uint8_t manuf_id = this->reg((uint8_t) CommandRegisters::MANUFAC_ID).get();
|
||||||
|
if (manuf_id != 0x05) { // 0x05 is Lite-On Semiconductor Corp. ID
|
||||||
|
ESP_LOGW(TAG, "Unknown manufacturer ID: 0x%02X", manuf_id);
|
||||||
|
this->mark_failed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Things getting not really funny here, we can't identify device type by part number ID
|
||||||
|
// ======================== ========= ===== =================
|
||||||
|
// Device Part ID Rev Capabilities
|
||||||
|
// ======================== ========= ===== =================
|
||||||
|
// Ltr-329/ltr-303 0x0a 0x00 Als 16b
|
||||||
|
// Ltr-553/ltr-556/ltr-556 0x09 0x02 Als 16b + Ps 11b diff nm sens
|
||||||
|
// Ltr-659 0x09 0x02 Ps 11b and ps gain
|
||||||
|
//
|
||||||
|
// There are other devices which might potentially work with default settings,
|
||||||
|
// but registers layout is different and we can't use them properly. For ex. ltr-558
|
||||||
|
|
||||||
|
PartIdRegister part_id{0};
|
||||||
|
part_id.raw = this->reg((uint8_t) CommandRegisters::PART_ID).get();
|
||||||
|
if (part_id.part_number_id != 0x0a && part_id.part_number_id != 0x09) {
|
||||||
|
ESP_LOGW(TAG, "Unknown part number ID: 0x%02X. It might not work properly.", part_id.part_number_id);
|
||||||
|
this->status_set_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::configure_reset_() {
|
||||||
|
ESP_LOGV(TAG, "Resetting");
|
||||||
|
|
||||||
|
AlsControlRegister als_ctrl{0};
|
||||||
|
als_ctrl.sw_reset = true;
|
||||||
|
this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw;
|
||||||
|
delay(2);
|
||||||
|
|
||||||
|
uint8_t tries = MAX_TRIES;
|
||||||
|
do {
|
||||||
|
ESP_LOGV(TAG, "Waiting for chip to reset");
|
||||||
|
delay(2);
|
||||||
|
als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get();
|
||||||
|
} while (als_ctrl.sw_reset && tries--); // while sw reset bit is on - keep waiting
|
||||||
|
|
||||||
|
if (als_ctrl.sw_reset) {
|
||||||
|
ESP_LOGW(TAG, "Reset timed out");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::configure_als_() {
|
||||||
|
AlsControlRegister als_ctrl{0};
|
||||||
|
|
||||||
|
als_ctrl.sw_reset = false;
|
||||||
|
als_ctrl.active_mode = true;
|
||||||
|
als_ctrl.gain = this->gain_;
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Setting active mode and gain reg 0x%02X", als_ctrl.raw);
|
||||||
|
this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw;
|
||||||
|
delay(5);
|
||||||
|
|
||||||
|
uint8_t tries = MAX_TRIES;
|
||||||
|
do {
|
||||||
|
ESP_LOGV(TAG, "Waiting for device to become active...");
|
||||||
|
delay(2);
|
||||||
|
als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get();
|
||||||
|
} while (!als_ctrl.active_mode && tries--); // while active mode is not set - keep waiting
|
||||||
|
|
||||||
|
if (!als_ctrl.active_mode) {
|
||||||
|
ESP_LOGW(TAG, "Failed to activate device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::configure_ps_() {
|
||||||
|
PsMeasurementRateRegister ps_meas{0};
|
||||||
|
ps_meas.ps_measurement_rate = PsMeasurementRate::PS_MEAS_RATE_50MS;
|
||||||
|
this->reg((uint8_t) CommandRegisters::PS_MEAS_RATE) = ps_meas.raw;
|
||||||
|
|
||||||
|
PsControlRegister ps_ctrl{0};
|
||||||
|
ps_ctrl.ps_mode_active = true;
|
||||||
|
ps_ctrl.ps_mode_xxx = true;
|
||||||
|
this->reg((uint8_t) CommandRegisters::PS_CONTR) = ps_ctrl.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t LTRAlsPsComponent::read_ps_data_() {
|
||||||
|
AlsPsStatusRegister als_status{0};
|
||||||
|
als_status.raw = this->reg((uint8_t) CommandRegisters::ALS_PS_STATUS).get();
|
||||||
|
if (!als_status.ps_new_data || als_status.data_invalid) {
|
||||||
|
return this->ps_readings_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ps_low = this->reg((uint8_t) CommandRegisters::PS_DATA_0).get();
|
||||||
|
PsData1Register ps_high;
|
||||||
|
ps_high.raw = this->reg((uint8_t) CommandRegisters::PS_DATA_1).get();
|
||||||
|
|
||||||
|
uint16_t val = encode_uint16(ps_high.ps_data_high, ps_low);
|
||||||
|
if (ps_high.ps_saturation_flag) {
|
||||||
|
return 0x7ff; // full 11 bit range
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::configure_gain_(AlsGain gain) {
|
||||||
|
AlsControlRegister als_ctrl{0};
|
||||||
|
als_ctrl.active_mode = true;
|
||||||
|
als_ctrl.gain = gain;
|
||||||
|
this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw;
|
||||||
|
delay(2);
|
||||||
|
|
||||||
|
AlsControlRegister read_als_ctrl{0};
|
||||||
|
read_als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get();
|
||||||
|
if (read_als_ctrl.gain != gain) {
|
||||||
|
ESP_LOGW(TAG, "Failed to set gain. We will try one more time.");
|
||||||
|
this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw;
|
||||||
|
delay(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::configure_integration_time_(IntegrationTime time) {
|
||||||
|
MeasurementRateRegister meas{0};
|
||||||
|
meas.measurement_repeat_rate = this->repeat_rate_;
|
||||||
|
meas.integration_time = time;
|
||||||
|
this->reg((uint8_t) CommandRegisters::MEAS_RATE) = meas.raw;
|
||||||
|
delay(2);
|
||||||
|
|
||||||
|
MeasurementRateRegister read_meas{0};
|
||||||
|
read_meas.raw = this->reg((uint8_t) CommandRegisters::MEAS_RATE).get();
|
||||||
|
if (read_meas.integration_time != time) {
|
||||||
|
ESP_LOGW(TAG, "Failed to set integration time. We will try one more time.");
|
||||||
|
this->reg((uint8_t) CommandRegisters::MEAS_RATE) = meas.raw;
|
||||||
|
delay(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataAvail LTRAlsPsComponent::is_als_data_ready_(AlsReadings &data) {
|
||||||
|
AlsPsStatusRegister als_status{0};
|
||||||
|
|
||||||
|
als_status.raw = this->reg((uint8_t) CommandRegisters::ALS_PS_STATUS).get();
|
||||||
|
if (!als_status.als_new_data)
|
||||||
|
return DataAvail::NO_DATA;
|
||||||
|
|
||||||
|
if (als_status.data_invalid) {
|
||||||
|
ESP_LOGW(TAG, "Data available but not valid");
|
||||||
|
return DataAvail::BAD_DATA;
|
||||||
|
}
|
||||||
|
ESP_LOGV(TAG, "Data ready, reported gain is %.0f", get_gain_coeff(als_status.gain));
|
||||||
|
if (data.gain != als_status.gain) {
|
||||||
|
ESP_LOGW(TAG, "Actual gain differs from requested (%.0f)", get_gain_coeff(data.gain));
|
||||||
|
return DataAvail::BAD_DATA;
|
||||||
|
}
|
||||||
|
return DataAvail::DATA_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::read_sensor_data_(AlsReadings &data) {
|
||||||
|
data.ch1 = 0;
|
||||||
|
data.ch0 = 0;
|
||||||
|
uint8_t ch1_0 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH1_0).get();
|
||||||
|
uint8_t ch1_1 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH1_1).get();
|
||||||
|
uint8_t ch0_0 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH0_0).get();
|
||||||
|
uint8_t ch0_1 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH0_1).get();
|
||||||
|
data.ch1 = encode_uint16(ch1_1, ch1_0);
|
||||||
|
data.ch0 = encode_uint16(ch0_1, ch0_0);
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Got sensor data: CH1 = %d, CH0 = %d", data.ch1, data.ch0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LTRAlsPsComponent::are_adjustments_required_(AlsReadings &data) {
|
||||||
|
if (!this->automatic_mode_enabled_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (data.number_of_adjustments > 15) {
|
||||||
|
// sometimes sensors fail to change sensitivity. this prevents us from infinite loop
|
||||||
|
ESP_LOGW(TAG, "Too many sensitivity adjustments done. Apparently, sensor reconfiguration fails. Stopping.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data.number_of_adjustments++;
|
||||||
|
|
||||||
|
// Recommended thresholds as per datasheet
|
||||||
|
static const uint16_t LOW_INTENSITY_THRESHOLD = 1000;
|
||||||
|
static const uint16_t HIGH_INTENSITY_THRESHOLD = 30000;
|
||||||
|
static const AlsGain GAINS[GAINS_COUNT] = {GAIN_1, GAIN_2, GAIN_4, GAIN_8, GAIN_48, GAIN_96};
|
||||||
|
static const IntegrationTime INT_TIMES[TIMES_COUNT] = {
|
||||||
|
INTEGRATION_TIME_50MS, INTEGRATION_TIME_100MS, INTEGRATION_TIME_150MS, INTEGRATION_TIME_200MS,
|
||||||
|
INTEGRATION_TIME_250MS, INTEGRATION_TIME_300MS, INTEGRATION_TIME_350MS, INTEGRATION_TIME_400MS};
|
||||||
|
|
||||||
|
if (data.ch0 <= LOW_INTENSITY_THRESHOLD) {
|
||||||
|
AlsGain next_gain = get_next(GAINS, data.gain);
|
||||||
|
if (next_gain != data.gain) {
|
||||||
|
data.gain = next_gain;
|
||||||
|
ESP_LOGV(TAG, "Low illuminance. Increasing gain.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
IntegrationTime next_time = get_next(INT_TIMES, data.integration_time);
|
||||||
|
if (next_time != data.integration_time) {
|
||||||
|
data.integration_time = next_time;
|
||||||
|
ESP_LOGV(TAG, "Low illuminance. Increasing integration time.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (data.ch0 >= HIGH_INTENSITY_THRESHOLD) {
|
||||||
|
AlsGain prev_gain = get_prev(GAINS, data.gain);
|
||||||
|
if (prev_gain != data.gain) {
|
||||||
|
data.gain = prev_gain;
|
||||||
|
ESP_LOGV(TAG, "High illuminance. Decreasing gain.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
IntegrationTime prev_time = get_prev(INT_TIMES, data.integration_time);
|
||||||
|
if (prev_time != data.integration_time) {
|
||||||
|
data.integration_time = prev_time;
|
||||||
|
ESP_LOGV(TAG, "High illuminance. Decreasing integration time.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Illuminance is sufficient.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Can't adjust sensitivity anymore.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::apply_lux_calculation_(AlsReadings &data) {
|
||||||
|
if ((data.ch0 == 0xFFFF) || (data.ch1 == 0xFFFF)) {
|
||||||
|
ESP_LOGW(TAG, "Sensors got saturated");
|
||||||
|
data.lux = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data.ch0 == 0x0000) && (data.ch1 == 0x0000)) {
|
||||||
|
ESP_LOGW(TAG, "Sensors blacked out");
|
||||||
|
data.lux = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ch0 = data.ch0;
|
||||||
|
float ch1 = data.ch1;
|
||||||
|
float ratio = ch1 / (ch0 + ch1);
|
||||||
|
float als_gain = get_gain_coeff(data.gain);
|
||||||
|
float als_time = ((float) get_itime_ms(data.integration_time)) / 100.0f;
|
||||||
|
float inv_pfactor = this->glass_attenuation_factor_;
|
||||||
|
float lux = 0.0f;
|
||||||
|
|
||||||
|
if (ratio < 0.45) {
|
||||||
|
lux = (1.7743 * ch0 + 1.1059 * ch1);
|
||||||
|
} else if (ratio < 0.64 && ratio >= 0.45) {
|
||||||
|
lux = (4.2785 * ch0 - 1.9548 * ch1);
|
||||||
|
} else if (ratio < 0.85 && ratio >= 0.64) {
|
||||||
|
lux = (0.5926 * ch0 + 0.1185 * ch1);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Impossible ch1/(ch0 + ch1) ratio");
|
||||||
|
lux = 0.0f;
|
||||||
|
}
|
||||||
|
lux = inv_pfactor * lux / als_gain / als_time;
|
||||||
|
data.lux = lux;
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Lux calculation: ratio %.3f, gain %.0fx, int time %.1f, inv_pfactor %.3f, lux %.3f", ratio, als_gain,
|
||||||
|
als_time, inv_pfactor, lux);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::publish_data_part_1_(AlsReadings &data) {
|
||||||
|
if (this->proximity_counts_sensor_ != nullptr) {
|
||||||
|
this->proximity_counts_sensor_->publish_state(this->ps_readings_);
|
||||||
|
}
|
||||||
|
if (this->ambient_light_sensor_ != nullptr) {
|
||||||
|
this->ambient_light_sensor_->publish_state(data.lux);
|
||||||
|
}
|
||||||
|
if (this->infrared_counts_sensor_ != nullptr) {
|
||||||
|
this->infrared_counts_sensor_->publish_state(data.ch1);
|
||||||
|
}
|
||||||
|
if (this->full_spectrum_counts_sensor_ != nullptr) {
|
||||||
|
this->full_spectrum_counts_sensor_->publish_state(data.ch0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTRAlsPsComponent::publish_data_part_2_(AlsReadings &data) {
|
||||||
|
if (this->actual_gain_sensor_ != nullptr) {
|
||||||
|
this->actual_gain_sensor_->publish_state(get_gain_coeff(data.gain));
|
||||||
|
}
|
||||||
|
if (this->actual_integration_time_sensor_ != nullptr) {
|
||||||
|
this->actual_integration_time_sensor_->publish_state(get_itime_ms(data.integration_time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace ltr_als_ps
|
||||||
|
} // namespace esphome
|
184
esphome/components/ltr_als_ps/ltr_als_ps.h
Normal file
184
esphome/components/ltr_als_ps/ltr_als_ps.h
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/optional.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
|
||||||
|
#include "ltr_definitions.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ltr_als_ps {
|
||||||
|
|
||||||
|
enum DataAvail : uint8_t { NO_DATA, BAD_DATA, DATA_OK };
|
||||||
|
|
||||||
|
enum LtrType : uint8_t {
|
||||||
|
LTR_TYPE_UNKNOWN = 0,
|
||||||
|
LTR_TYPE_ALS_ONLY = 1,
|
||||||
|
LTR_TYPE_PS_ONLY = 2,
|
||||||
|
LTR_TYPE_ALS_AND_PS = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
class LTRAlsPsComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
//
|
||||||
|
// EspHome framework functions
|
||||||
|
//
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
void update() override;
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
// Configuration setters : General
|
||||||
|
//
|
||||||
|
void set_ltr_type(LtrType type) { this->ltr_type_ = type; }
|
||||||
|
|
||||||
|
// Configuration setters : ALS
|
||||||
|
//
|
||||||
|
void set_als_auto_mode(bool enable) { this->automatic_mode_enabled_ = enable; }
|
||||||
|
void set_als_gain(AlsGain gain) { this->gain_ = gain; }
|
||||||
|
void set_als_integration_time(IntegrationTime time) { this->integration_time_ = time; }
|
||||||
|
void set_als_meas_repeat_rate(MeasurementRepeatRate rate) { this->repeat_rate_ = rate; }
|
||||||
|
void set_als_glass_attenuation_factor(float factor) { this->glass_attenuation_factor_ = factor; }
|
||||||
|
|
||||||
|
// Configuration setters : PS
|
||||||
|
//
|
||||||
|
void set_ps_high_threshold(uint16_t threshold) { this->ps_threshold_high_ = threshold; }
|
||||||
|
void set_ps_low_threshold(uint16_t threshold) { this->ps_threshold_low_ = threshold; }
|
||||||
|
void set_ps_cooldown_time_s(uint16_t time) { this->ps_cooldown_time_s_ = time; }
|
||||||
|
void set_ps_gain(PsGain gain) { this->ps_gain_ = gain; }
|
||||||
|
|
||||||
|
// Sensors setters
|
||||||
|
//
|
||||||
|
void set_ambient_light_sensor(sensor::Sensor *sensor) { this->ambient_light_sensor_ = sensor; }
|
||||||
|
void set_full_spectrum_counts_sensor(sensor::Sensor *sensor) { this->full_spectrum_counts_sensor_ = sensor; }
|
||||||
|
void set_infrared_counts_sensor(sensor::Sensor *sensor) { this->infrared_counts_sensor_ = sensor; }
|
||||||
|
void set_actual_gain_sensor(sensor::Sensor *sensor) { this->actual_gain_sensor_ = sensor; }
|
||||||
|
void set_actual_integration_time_sensor(sensor::Sensor *sensor) { this->actual_integration_time_sensor_ = sensor; }
|
||||||
|
void set_proximity_counts_sensor(sensor::Sensor *sensor) { this->proximity_counts_sensor_ = sensor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//
|
||||||
|
// Internal state machine, used to split all the actions into
|
||||||
|
// small steps in loop() to make sure we are not blocking execution
|
||||||
|
//
|
||||||
|
enum class State : uint8_t {
|
||||||
|
NOT_INITIALIZED,
|
||||||
|
DELAYED_SETUP,
|
||||||
|
IDLE,
|
||||||
|
WAITING_FOR_DATA,
|
||||||
|
COLLECTING_DATA_AUTO,
|
||||||
|
DATA_COLLECTED,
|
||||||
|
ADJUSTMENT_IN_PROGRESS,
|
||||||
|
READY_TO_PUBLISH,
|
||||||
|
KEEP_PUBLISHING
|
||||||
|
} state_{State::NOT_INITIALIZED};
|
||||||
|
|
||||||
|
LtrType ltr_type_{LtrType::LTR_TYPE_ALS_ONLY};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Current measurements data
|
||||||
|
//
|
||||||
|
struct AlsReadings {
|
||||||
|
uint16_t ch0{0};
|
||||||
|
uint16_t ch1{0};
|
||||||
|
AlsGain gain{AlsGain::GAIN_1};
|
||||||
|
IntegrationTime integration_time{IntegrationTime::INTEGRATION_TIME_100MS};
|
||||||
|
float lux{0.0f};
|
||||||
|
uint8_t number_of_adjustments{0};
|
||||||
|
} als_readings_;
|
||||||
|
uint16_t ps_readings_{0xfffe};
|
||||||
|
|
||||||
|
inline bool is_als_() const {
|
||||||
|
return this->ltr_type_ == LtrType::LTR_TYPE_ALS_ONLY || this->ltr_type_ == LtrType::LTR_TYPE_ALS_AND_PS;
|
||||||
|
}
|
||||||
|
inline bool is_ps_() const {
|
||||||
|
return this->ltr_type_ == LtrType::LTR_TYPE_PS_ONLY || this->ltr_type_ == LtrType::LTR_TYPE_ALS_AND_PS;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Device interaction and data manipulation
|
||||||
|
//
|
||||||
|
bool check_part_number_();
|
||||||
|
|
||||||
|
void configure_reset_();
|
||||||
|
void configure_als_();
|
||||||
|
void configure_integration_time_(IntegrationTime time);
|
||||||
|
void configure_gain_(AlsGain gain);
|
||||||
|
DataAvail is_als_data_ready_(AlsReadings &data);
|
||||||
|
void read_sensor_data_(AlsReadings &data);
|
||||||
|
bool are_adjustments_required_(AlsReadings &data);
|
||||||
|
void apply_lux_calculation_(AlsReadings &data);
|
||||||
|
void publish_data_part_1_(AlsReadings &data);
|
||||||
|
void publish_data_part_2_(AlsReadings &data);
|
||||||
|
|
||||||
|
void configure_ps_();
|
||||||
|
uint16_t read_ps_data_();
|
||||||
|
void check_and_trigger_ps_();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Component configuration
|
||||||
|
//
|
||||||
|
bool automatic_mode_enabled_{true};
|
||||||
|
AlsGain gain_{AlsGain::GAIN_1};
|
||||||
|
IntegrationTime integration_time_{IntegrationTime::INTEGRATION_TIME_100MS};
|
||||||
|
MeasurementRepeatRate repeat_rate_{MeasurementRepeatRate::REPEAT_RATE_500MS};
|
||||||
|
float glass_attenuation_factor_{1.0};
|
||||||
|
|
||||||
|
uint16_t ps_cooldown_time_s_{5};
|
||||||
|
PsGain ps_gain_{PsGain::PS_GAIN_16};
|
||||||
|
uint16_t ps_threshold_high_{0xffff};
|
||||||
|
uint16_t ps_threshold_low_{0x0000};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sensors for publishing data
|
||||||
|
//
|
||||||
|
sensor::Sensor *infrared_counts_sensor_{nullptr}; // direct reading CH1, infrared only
|
||||||
|
sensor::Sensor *full_spectrum_counts_sensor_{nullptr}; // direct reading CH0, infrared + visible light
|
||||||
|
sensor::Sensor *ambient_light_sensor_{nullptr}; // calculated lux
|
||||||
|
sensor::Sensor *actual_gain_sensor_{nullptr}; // actual gain of reading
|
||||||
|
sensor::Sensor *actual_integration_time_sensor_{nullptr}; // actual integration time
|
||||||
|
sensor::Sensor *proximity_counts_sensor_{nullptr}; // proximity sensor
|
||||||
|
|
||||||
|
bool is_any_als_sensor_enabled_() const {
|
||||||
|
return this->ambient_light_sensor_ != nullptr || this->full_spectrum_counts_sensor_ != nullptr ||
|
||||||
|
this->infrared_counts_sensor_ != nullptr || this->actual_gain_sensor_ != nullptr ||
|
||||||
|
this->actual_integration_time_sensor_ != nullptr;
|
||||||
|
}
|
||||||
|
bool is_any_ps_sensor_enabled_() const { return this->proximity_counts_sensor_ != nullptr; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Trigger section for the automations
|
||||||
|
//
|
||||||
|
friend class LTRPsHighTrigger;
|
||||||
|
friend class LTRPsLowTrigger;
|
||||||
|
|
||||||
|
CallbackManager<void()> on_ps_high_trigger_callback_;
|
||||||
|
CallbackManager<void()> on_ps_low_trigger_callback_;
|
||||||
|
|
||||||
|
void add_on_ps_high_trigger_callback_(std::function<void()> callback) {
|
||||||
|
this->on_ps_high_trigger_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_on_ps_low_trigger_callback_(std::function<void()> callback) {
|
||||||
|
this->on_ps_low_trigger_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LTRPsHighTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit LTRPsHighTrigger(LTRAlsPsComponent *parent) {
|
||||||
|
parent->add_on_ps_high_trigger_callback_([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LTRPsLowTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit LTRPsLowTrigger(LTRAlsPsComponent *parent) {
|
||||||
|
parent->add_on_ps_low_trigger_callback_([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace ltr_als_ps
|
||||||
|
} // namespace esphome
|
275
esphome/components/ltr_als_ps/ltr_definitions.h
Normal file
275
esphome/components/ltr_als_ps/ltr_definitions.h
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ltr_als_ps {
|
||||||
|
|
||||||
|
enum class CommandRegisters : uint8_t {
|
||||||
|
ALS_CONTR = 0x80, // ALS operation mode control and SW reset
|
||||||
|
PS_CONTR = 0x81, // PS operation mode control
|
||||||
|
PS_LED = 0x82, // PS LED pulse frequency control
|
||||||
|
PS_N_PULSES = 0x83, // PS number of pulses control
|
||||||
|
PS_MEAS_RATE = 0x84, // PS measurement rate in active mode
|
||||||
|
MEAS_RATE = 0x85, // ALS measurement rate in active mode
|
||||||
|
PART_ID = 0x86, // Part Number ID and Revision ID
|
||||||
|
MANUFAC_ID = 0x87, // Manufacturer ID
|
||||||
|
ALS_DATA_CH1_0 = 0x88, // ALS measurement CH1 data, lower byte - infrared only
|
||||||
|
ALS_DATA_CH1_1 = 0x89, // ALS measurement CH1 data, upper byte - infrared only
|
||||||
|
ALS_DATA_CH0_0 = 0x8A, // ALS measurement CH0 data, lower byte - visible + infrared
|
||||||
|
ALS_DATA_CH0_1 = 0x8B, // ALS measurement CH0 data, upper byte - visible + infrared
|
||||||
|
ALS_PS_STATUS = 0x8C, // ALS PS new data status
|
||||||
|
PS_DATA_0 = 0x8D, // PS measurement data, lower byte
|
||||||
|
PS_DATA_1 = 0x8E, // PS measurement data, upper byte
|
||||||
|
ALS_PS_INTERRUPT = 0x8F, // Interrupt status
|
||||||
|
PS_THRES_UP_0 = 0x90, // PS interrupt upper threshold, lower byte
|
||||||
|
PS_THRES_UP_1 = 0x91, // PS interrupt upper threshold, upper byte
|
||||||
|
PS_THRES_LOW_0 = 0x92, // PS interrupt lower threshold, lower byte
|
||||||
|
PS_THRES_LOW_1 = 0x93, // PS interrupt lower threshold, upper byte
|
||||||
|
PS_OFFSET_1 = 0x94, // PS offset, upper byte
|
||||||
|
PS_OFFSET_0 = 0x95, // PS offset, lower byte
|
||||||
|
// 0x96 - reserved
|
||||||
|
ALS_THRES_UP_0 = 0x97, // ALS interrupt upper threshold, lower byte
|
||||||
|
ALS_THRES_UP_1 = 0x98, // ALS interrupt upper threshold, upper byte
|
||||||
|
ALS_THRES_LOW_0 = 0x99, // ALS interrupt lower threshold, lower byte
|
||||||
|
ALS_THRES_LOW_1 = 0x9A, // ALS interrupt lower threshold, upper byte
|
||||||
|
// 0x9B - reserved
|
||||||
|
// 0x9C - reserved
|
||||||
|
// 0x9D - reserved
|
||||||
|
INTERRUPT_PERSIST = 0x9E // Interrupt persistence filter
|
||||||
|
};
|
||||||
|
|
||||||
|
// ALS Sensor gain levels
|
||||||
|
enum AlsGain : uint8_t {
|
||||||
|
GAIN_1 = 0, // default
|
||||||
|
GAIN_2 = 1,
|
||||||
|
GAIN_4 = 2,
|
||||||
|
GAIN_8 = 3,
|
||||||
|
GAIN_48 = 6,
|
||||||
|
GAIN_96 = 7,
|
||||||
|
};
|
||||||
|
static const uint8_t GAINS_COUNT = 6;
|
||||||
|
|
||||||
|
// ALS Sensor integration times
|
||||||
|
enum IntegrationTime : uint8_t {
|
||||||
|
INTEGRATION_TIME_100MS = 0, // default
|
||||||
|
INTEGRATION_TIME_50MS = 1,
|
||||||
|
INTEGRATION_TIME_200MS = 2,
|
||||||
|
INTEGRATION_TIME_400MS = 3,
|
||||||
|
INTEGRATION_TIME_150MS = 4,
|
||||||
|
INTEGRATION_TIME_250MS = 5,
|
||||||
|
INTEGRATION_TIME_300MS = 6,
|
||||||
|
INTEGRATION_TIME_350MS = 7
|
||||||
|
};
|
||||||
|
static const uint8_t TIMES_COUNT = 8;
|
||||||
|
|
||||||
|
// ALS Sensor measurement repeat rate
|
||||||
|
enum MeasurementRepeatRate {
|
||||||
|
REPEAT_RATE_50MS = 0,
|
||||||
|
REPEAT_RATE_100MS = 1,
|
||||||
|
REPEAT_RATE_200MS = 2,
|
||||||
|
REPEAT_RATE_500MS = 3, // default
|
||||||
|
REPEAT_RATE_1000MS = 4,
|
||||||
|
REPEAT_RATE_2000MS = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
// PS Sensor gain levels
|
||||||
|
enum PsGain : uint8_t {
|
||||||
|
PS_GAIN_16 = 0, // default
|
||||||
|
PS_GAIN_32 = 2,
|
||||||
|
PS_GAIN_64 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// PS Mode
|
||||||
|
enum PsMode : uint8_t {
|
||||||
|
PS_MODE_STANDBY_00 = 0, // default
|
||||||
|
PS_MODE_STANDBY_01 = 1,
|
||||||
|
PS_MODE_ACTIVE_10 = 2,
|
||||||
|
PS_MODE_ACTIVE_11 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED Pulse Modulation Frequency
|
||||||
|
enum PsLedFreq : uint8_t {
|
||||||
|
PS_LED_FREQ_30KHZ = 0,
|
||||||
|
PS_LED_FREQ_40KHZ = 1,
|
||||||
|
PS_LED_FREQ_50KHZ = 2,
|
||||||
|
PS_LED_FREQ_60KHZ = 3, // default
|
||||||
|
PS_LED_FREQ_70KHZ = 4,
|
||||||
|
PS_LED_FREQ_80KHZ = 5,
|
||||||
|
PS_LED_FREQ_90KHZ = 6,
|
||||||
|
PS_LED_FREQ_100KHZ = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED current duty
|
||||||
|
enum PsLedDuty : uint8_t {
|
||||||
|
PS_LED_DUTY_25 = 0,
|
||||||
|
PS_LED_DUTY_50 = 1,
|
||||||
|
PS_LED_DUTY_75 = 2,
|
||||||
|
PS_LED_DUTY_100 = 3, // default
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED pulsed current level
|
||||||
|
enum PsLedCurrent : uint8_t {
|
||||||
|
PS_LED_CURRENT_5MA = 0,
|
||||||
|
PS_LED_CURRENT_10MA = 1,
|
||||||
|
PS_LED_CURRENT_20MA = 2,
|
||||||
|
PS_LED_CURRENT_50MA = 3,
|
||||||
|
PS_LED_CURRENT_100MA = 4, // default
|
||||||
|
PS_LED_CURRENT_100MA1 = 5,
|
||||||
|
PS_LED_CURRENT_100MA2 = 6,
|
||||||
|
PS_LED_CURRENT_100MA3 = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
// PS measurement rate
|
||||||
|
enum PsMeasurementRate : uint8_t {
|
||||||
|
PS_MEAS_RATE_50MS = 0,
|
||||||
|
PS_MEAS_RATE_70MS = 1,
|
||||||
|
PS_MEAS_RATE_100MS = 2,
|
||||||
|
PS_MEAS_RATE_200MS = 3,
|
||||||
|
PS_MEAS_RATE_500MS = 4, // default
|
||||||
|
PS_MEAS_RATE_1000MS = 5,
|
||||||
|
PS_MEAS_RATE_2000MS = 6,
|
||||||
|
PS_MEAS_RATE_2000MS1 = 7,
|
||||||
|
PS_MEAS_RATE_10MS = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ALS_CONTR Register (0x80)
|
||||||
|
//
|
||||||
|
union AlsControlRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
bool active_mode : 1;
|
||||||
|
bool sw_reset : 1;
|
||||||
|
AlsGain gain : 3;
|
||||||
|
uint8_t reserved : 3;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// PS_CONTR Register (0x81)
|
||||||
|
//
|
||||||
|
union PsControlRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
bool ps_mode_xxx : 1;
|
||||||
|
bool ps_mode_active : 1;
|
||||||
|
PsGain ps_gain : 2; // only LTR-659/558
|
||||||
|
bool reserved_4 : 1;
|
||||||
|
bool ps_saturation_indicator_enable : 1;
|
||||||
|
bool reserved_6 : 1;
|
||||||
|
bool reserved_7 : 1;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// PS_LED Register (0x82)
|
||||||
|
//
|
||||||
|
union PsLedRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
PsLedCurrent ps_led_current : 3;
|
||||||
|
PsLedDuty ps_led_duty : 2;
|
||||||
|
PsLedFreq ps_led_freq : 3;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// PS_N_PULSES Register (0x83)
|
||||||
|
//
|
||||||
|
union PsNPulsesRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
uint8_t number_of_pulses : 4;
|
||||||
|
uint8_t reserved : 4;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// PS_MEAS_RATE Register (0x84)
|
||||||
|
//
|
||||||
|
union PsMeasurementRateRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
PsMeasurementRate ps_measurement_rate : 4;
|
||||||
|
uint8_t reserved : 4;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ALS_MEAS_RATE Register (0x85)
|
||||||
|
//
|
||||||
|
union MeasurementRateRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
MeasurementRepeatRate measurement_repeat_rate : 3;
|
||||||
|
IntegrationTime integration_time : 3;
|
||||||
|
bool reserved_6 : 1;
|
||||||
|
bool reserved_7 : 1;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// PART_ID Register (0x86) (Read Only)
|
||||||
|
//
|
||||||
|
union PartIdRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
uint8_t part_number_id : 4;
|
||||||
|
uint8_t revision_id : 4;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// ALS_PS_STATUS Register (0x8C) (Read Only)
|
||||||
|
//
|
||||||
|
union AlsPsStatusRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
bool ps_new_data : 1; // 0 - old data, 1 - new data
|
||||||
|
bool ps_interrupt : 1; // 0 - interrupt signal not active, 1 - interrupt signal active
|
||||||
|
bool als_new_data : 1; // 0 - old data, 1 - new data
|
||||||
|
bool als_interrupt : 1; // 0 - interrupt signal not active, 1 - interrupt signal active
|
||||||
|
AlsGain gain : 3; // current ALS gain
|
||||||
|
bool data_invalid : 1;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// PS_DATA_1 Register (0x8E) (Read Only)
|
||||||
|
//
|
||||||
|
union PsData1Register {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
uint8_t ps_data_high : 3;
|
||||||
|
uint8_t reserved : 4;
|
||||||
|
bool ps_saturation_flag : 1;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// INTERRUPT Register (0x8F) (Read Only)
|
||||||
|
//
|
||||||
|
union InterruptRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
bool ps_interrupt : 1;
|
||||||
|
bool als_interrupt : 1;
|
||||||
|
bool interrupt_polarity : 1; // 0 - active low (default), 1 - active high
|
||||||
|
uint8_t reserved : 5;
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// INTERRUPT_PERSIST Register (0x9E)
|
||||||
|
//
|
||||||
|
union InterruptPersistRegister {
|
||||||
|
uint8_t raw;
|
||||||
|
struct {
|
||||||
|
uint8_t als_persist : 4; // 0 - every ALS cycle, 1 - every 2 ALS cycles, ... 15 - every 16 ALS cycles
|
||||||
|
uint8_t ps_persist : 4; // 0 - every PS cycle, 1 - every 2 PS cycles, ... 15 - every 16 PS cycles
|
||||||
|
} __attribute__((packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ltr_als_ps
|
||||||
|
} // namespace esphome
|
271
esphome/components/ltr_als_ps/sensor.py
Normal file
271
esphome/components/ltr_als_ps/sensor.py
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ACTUAL_GAIN,
|
||||||
|
CONF_AMBIENT_LIGHT,
|
||||||
|
CONF_AUTO_MODE,
|
||||||
|
CONF_GAIN,
|
||||||
|
CONF_GLASS_ATTENUATION_FACTOR,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_INTEGRATION_TIME,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_REPEAT,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
|
CONF_TYPE,
|
||||||
|
DEVICE_CLASS_DISTANCE,
|
||||||
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
ICON_BRIGHTNESS_5,
|
||||||
|
ICON_BRIGHTNESS_6,
|
||||||
|
ICON_TIMER,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_LUX,
|
||||||
|
UNIT_MILLISECOND,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@latonita"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
CONF_ACTUAL_INTEGRATION_TIME = "actual_integration_time"
|
||||||
|
CONF_FULL_SPECTRUM_COUNTS = "full_spectrum_counts"
|
||||||
|
CONF_INFRARED_COUNTS = "infrared_counts"
|
||||||
|
CONF_ON_PS_HIGH_THRESHOLD = "on_ps_high_threshold"
|
||||||
|
CONF_ON_PS_LOW_THRESHOLD = "on_ps_low_threshold"
|
||||||
|
CONF_PS_COOLDOWN = "ps_cooldown"
|
||||||
|
CONF_PS_COUNTS = "ps_counts"
|
||||||
|
CONF_PS_GAIN = "ps_gain"
|
||||||
|
CONF_PS_HIGH_THRESHOLD = "ps_high_threshold"
|
||||||
|
CONF_PS_LOW_THRESHOLD = "ps_low_threshold"
|
||||||
|
ICON_BRIGHTNESS_7 = "mdi:brightness-7"
|
||||||
|
ICON_GAIN = "mdi:multiplication"
|
||||||
|
ICON_PROXIMITY = "mdi:hand-wave-outline"
|
||||||
|
UNIT_COUNTS = "#"
|
||||||
|
|
||||||
|
ltr_als_ps_ns = cg.esphome_ns.namespace("ltr_als_ps")
|
||||||
|
|
||||||
|
LTRAlsPsComponent = ltr_als_ps_ns.class_(
|
||||||
|
"LTRAlsPsComponent", cg.PollingComponent, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
LtrType = ltr_als_ps_ns.enum("LtrType")
|
||||||
|
LTR_TYPES = {
|
||||||
|
"ALS": LtrType.LTR_TYPE_ALS_ONLY,
|
||||||
|
"PS": LtrType.LTR_TYPE_PS_ONLY,
|
||||||
|
"ALS_PS": LtrType.LTR_TYPE_ALS_AND_PS,
|
||||||
|
}
|
||||||
|
|
||||||
|
AlsGain = ltr_als_ps_ns.enum("AlsGain")
|
||||||
|
ALS_GAINS = {
|
||||||
|
"1X": AlsGain.GAIN_1,
|
||||||
|
"2X": AlsGain.GAIN_2,
|
||||||
|
"4X": AlsGain.GAIN_4,
|
||||||
|
"8X": AlsGain.GAIN_8,
|
||||||
|
"48X": AlsGain.GAIN_48,
|
||||||
|
"96X": AlsGain.GAIN_96,
|
||||||
|
}
|
||||||
|
|
||||||
|
IntegrationTime = ltr_als_ps_ns.enum("IntegrationTime")
|
||||||
|
INTEGRATION_TIMES = {
|
||||||
|
50: IntegrationTime.INTEGRATION_TIME_50MS,
|
||||||
|
100: IntegrationTime.INTEGRATION_TIME_100MS,
|
||||||
|
150: IntegrationTime.INTEGRATION_TIME_150MS,
|
||||||
|
200: IntegrationTime.INTEGRATION_TIME_200MS,
|
||||||
|
250: IntegrationTime.INTEGRATION_TIME_250MS,
|
||||||
|
300: IntegrationTime.INTEGRATION_TIME_300MS,
|
||||||
|
350: IntegrationTime.INTEGRATION_TIME_350MS,
|
||||||
|
400: IntegrationTime.INTEGRATION_TIME_400MS,
|
||||||
|
}
|
||||||
|
|
||||||
|
MeasurementRepeatRate = ltr_als_ps_ns.enum("MeasurementRepeatRate")
|
||||||
|
MEASUREMENT_REPEAT_RATES = {
|
||||||
|
50: MeasurementRepeatRate.REPEAT_RATE_50MS,
|
||||||
|
100: MeasurementRepeatRate.REPEAT_RATE_100MS,
|
||||||
|
200: MeasurementRepeatRate.REPEAT_RATE_200MS,
|
||||||
|
500: MeasurementRepeatRate.REPEAT_RATE_500MS,
|
||||||
|
1000: MeasurementRepeatRate.REPEAT_RATE_1000MS,
|
||||||
|
2000: MeasurementRepeatRate.REPEAT_RATE_2000MS,
|
||||||
|
}
|
||||||
|
|
||||||
|
PsGain = ltr_als_ps_ns.enum("PsGain")
|
||||||
|
PS_GAINS = {
|
||||||
|
"16X": PsGain.PS_GAIN_16,
|
||||||
|
"32X": PsGain.PS_GAIN_32,
|
||||||
|
"64X": PsGain.PS_GAIN_64,
|
||||||
|
}
|
||||||
|
|
||||||
|
LTRPsHighTrigger = ltr_als_ps_ns.class_(
|
||||||
|
"LTRPsHighTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
LTRPsLowTrigger = ltr_als_ps_ns.class_("LTRPsLowTrigger", automation.Trigger.template())
|
||||||
|
|
||||||
|
|
||||||
|
def validate_integration_time(value):
|
||||||
|
value = cv.positive_time_period_milliseconds(value).total_milliseconds
|
||||||
|
return cv.enum(INTEGRATION_TIMES, int=True)(value)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_repeat_rate(value):
|
||||||
|
value = cv.positive_time_period_milliseconds(value).total_milliseconds
|
||||||
|
return cv.enum(MEASUREMENT_REPEAT_RATES, int=True)(value)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_time_and_repeat_rate(config):
|
||||||
|
integraton_time = config[CONF_INTEGRATION_TIME]
|
||||||
|
repeat_rate = config[CONF_REPEAT]
|
||||||
|
if integraton_time > repeat_rate:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Measurement repeat rate ({repeat_rate}ms) shall be greater or equal to integration time ({integraton_time}ms)"
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(LTRAlsPsComponent),
|
||||||
|
cv.Optional(CONF_TYPE, default="ALS_PS"): cv.enum(LTR_TYPES, upper=True),
|
||||||
|
cv.Optional(CONF_AUTO_MODE, default=True): cv.boolean,
|
||||||
|
cv.Optional(CONF_GAIN, default="1X"): cv.enum(ALS_GAINS, upper=True),
|
||||||
|
cv.Optional(
|
||||||
|
CONF_INTEGRATION_TIME, default="100ms"
|
||||||
|
): validate_integration_time,
|
||||||
|
cv.Optional(CONF_REPEAT, default="500ms"): validate_repeat_rate,
|
||||||
|
cv.Optional(CONF_GLASS_ATTENUATION_FACTOR, default=1.0): cv.float_range(
|
||||||
|
min=1.0
|
||||||
|
),
|
||||||
|
cv.Optional(
|
||||||
|
CONF_PS_COOLDOWN, default="5s"
|
||||||
|
): cv.positive_time_period_seconds,
|
||||||
|
cv.Optional(CONF_PS_GAIN, default="16X"): cv.enum(PS_GAINS, upper=True),
|
||||||
|
cv.Optional(CONF_PS_HIGH_THRESHOLD, default=65535): cv.int_range(
|
||||||
|
min=0, max=65535
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PS_LOW_THRESHOLD, default=0): cv.int_range(
|
||||||
|
min=0, max=65535
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_PS_HIGH_THRESHOLD): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsHighTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_PS_LOW_THRESHOLD): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsLowTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_AMBIENT_LIGHT): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_LUX,
|
||||||
|
icon=ICON_BRIGHTNESS_6,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_INFRARED_COUNTS): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_COUNTS,
|
||||||
|
icon=ICON_BRIGHTNESS_5,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_FULL_SPECTRUM_COUNTS): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_COUNTS,
|
||||||
|
icon=ICON_BRIGHTNESS_7,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PS_COUNTS): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_COUNTS,
|
||||||
|
icon=ICON_PROXIMITY,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_DISTANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACTUAL_GAIN): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
icon=ICON_GAIN,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACTUAL_INTEGRATION_TIME): cv.maybe_simple_value(
|
||||||
|
sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_MILLISECOND,
|
||||||
|
icon=ICON_TIMER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
key=CONF_NAME,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x29)),
|
||||||
|
validate_time_and_repeat_rate,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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 als_config := config.get(CONF_AMBIENT_LIGHT):
|
||||||
|
sens = await sensor.new_sensor(als_config)
|
||||||
|
cg.add(var.set_ambient_light_sensor(sens))
|
||||||
|
|
||||||
|
if infrared_cnt_config := config.get(CONF_INFRARED_COUNTS):
|
||||||
|
sens = await sensor.new_sensor(infrared_cnt_config)
|
||||||
|
cg.add(var.set_infrared_counts_sensor(sens))
|
||||||
|
|
||||||
|
if full_spect_cnt_config := config.get(CONF_FULL_SPECTRUM_COUNTS):
|
||||||
|
sens = await sensor.new_sensor(full_spect_cnt_config)
|
||||||
|
cg.add(var.set_full_spectrum_counts_sensor(sens))
|
||||||
|
|
||||||
|
if act_gain_config := config.get(CONF_ACTUAL_GAIN):
|
||||||
|
sens = await sensor.new_sensor(act_gain_config)
|
||||||
|
cg.add(var.set_actual_gain_sensor(sens))
|
||||||
|
|
||||||
|
if act_itime_config := config.get(CONF_ACTUAL_INTEGRATION_TIME):
|
||||||
|
sens = await sensor.new_sensor(act_itime_config)
|
||||||
|
cg.add(var.set_actual_integration_time_sensor(sens))
|
||||||
|
|
||||||
|
if prox_cnt_config := config.get(CONF_PS_COUNTS):
|
||||||
|
sens = await sensor.new_sensor(prox_cnt_config)
|
||||||
|
cg.add(var.set_proximity_counts_sensor(sens))
|
||||||
|
|
||||||
|
for prox_high_tr in config.get(CONF_ON_PS_HIGH_THRESHOLD, []):
|
||||||
|
trigger = cg.new_Pvariable(prox_high_tr[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], prox_high_tr)
|
||||||
|
|
||||||
|
for prox_low_tr in config.get(CONF_ON_PS_LOW_THRESHOLD, []):
|
||||||
|
trigger = cg.new_Pvariable(prox_low_tr[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], prox_low_tr)
|
||||||
|
|
||||||
|
cg.add(var.set_ltr_type(config[CONF_TYPE]))
|
||||||
|
|
||||||
|
cg.add(var.set_als_auto_mode(config[CONF_AUTO_MODE]))
|
||||||
|
cg.add(var.set_als_gain(config[CONF_GAIN]))
|
||||||
|
cg.add(var.set_als_integration_time(config[CONF_INTEGRATION_TIME]))
|
||||||
|
cg.add(var.set_als_meas_repeat_rate(config[CONF_REPEAT]))
|
||||||
|
cg.add(var.set_als_glass_attenuation_factor(config[CONF_GLASS_ATTENUATION_FACTOR]))
|
||||||
|
|
||||||
|
cg.add(var.set_ps_cooldown_time_s(config[CONF_PS_COOLDOWN]))
|
||||||
|
cg.add(var.set_ps_gain(config[CONF_PS_GAIN]))
|
||||||
|
cg.add(var.set_ps_high_threshold(config[CONF_PS_HIGH_THRESHOLD]))
|
||||||
|
cg.add(var.set_ps_low_threshold(config[CONF_PS_LOW_THRESHOLD]))
|
@ -29,7 +29,7 @@ enum MAX6956GPIORegisters {
|
|||||||
MAX6956_PORT_CONFIG_START = 0x09, // Port Configuration P7, P6, P5, P4
|
MAX6956_PORT_CONFIG_START = 0x09, // Port Configuration P7, P6, P5, P4
|
||||||
MAX6956_CURRENT_START = 0x12, // Current054
|
MAX6956_CURRENT_START = 0x12, // Current054
|
||||||
MAX6956_1PORT_VALUE_START = 0x20, // Port 0 only (virtual port, no action)
|
MAX6956_1PORT_VALUE_START = 0x20, // Port 0 only (virtual port, no action)
|
||||||
MAX6956_8PORTS_VALUE_START = 0x44, // 8 ports 4–11 (data bits D0–D7)
|
MAX6956_8PORTS_VALUE_START = 0x44, // 8 ports 4-11 (data bits D0-D7)
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MAX6956GPIOFlag { FLAG_LED = 0x20 };
|
enum MAX6956GPIOFlag { FLAG_LED = 0x20 };
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "mhz19.h"
|
#include "mhz19.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace mhz19 {
|
namespace mhz19 {
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ void MHZ19Component::update() {
|
|||||||
uint32_t now_ms = millis();
|
uint32_t now_ms = millis();
|
||||||
uint32_t warmup_ms = this->warmup_seconds_ * 1000;
|
uint32_t warmup_ms = this->warmup_seconds_ * 1000;
|
||||||
if (now_ms < warmup_ms) {
|
if (now_ms < warmup_ms) {
|
||||||
ESP_LOGW(TAG, "MHZ19 warming up, %ds left", (warmup_ms - now_ms) / 1000);
|
ESP_LOGW(TAG, "MHZ19 warming up, %" PRIu32 " s left", (warmup_ms - now_ms) / 1000);
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -110,7 +112,7 @@ void MHZ19Component::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Automatic baseline calibration disabled on boot");
|
ESP_LOGCONFIG(TAG, " Automatic baseline calibration disabled on boot");
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, " Warmup seconds: %ds", this->warmup_seconds_);
|
ESP_LOGCONFIG(TAG, " Warmup time: %" PRIu32 " s", this->warmup_seconds_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mhz19
|
} // namespace mhz19
|
||||||
|
@ -329,11 +329,14 @@ async def to_code(config):
|
|||||||
file: Path = base_dir / h.hexdigest()[:8] / model_config[CONF_FILE]
|
file: Path = base_dir / h.hexdigest()[:8] / model_config[CONF_FILE]
|
||||||
|
|
||||||
elif model_config[CONF_TYPE] == TYPE_LOCAL:
|
elif model_config[CONF_TYPE] == TYPE_LOCAL:
|
||||||
file = model_config[CONF_PATH]
|
file = Path(model_config[CONF_PATH])
|
||||||
|
|
||||||
elif model_config[CONF_TYPE] == TYPE_HTTP:
|
elif model_config[CONF_TYPE] == TYPE_HTTP:
|
||||||
file = _compute_local_file_path(model_config) / "manifest.json"
|
file = _compute_local_file_path(model_config) / "manifest.json"
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError("Unsupported config type: {model_config[CONF_TYPE]}")
|
||||||
|
|
||||||
manifest, data = _load_model_data(file)
|
manifest, data = _load_model_data(file)
|
||||||
|
|
||||||
rhs = [HexInt(x) for x in data]
|
rhs = [HexInt(x) for x in data]
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <tensorflow/lite/micro/micro_interpreter.h>
|
#include <tensorflow/lite/micro/micro_interpreter.h>
|
||||||
#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
|
#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@ -316,7 +317,7 @@ float MicroWakeWord::perform_streaming_inference_() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Streaming Inference Latency=%u ms", (millis() - prior_invoke));
|
ESP_LOGV(TAG, "Streaming Inference Latency=%" PRIu32 " ms", (millis() - prior_invoke));
|
||||||
|
|
||||||
TfLiteTensor *output = this->streaming_interpreter_->output(0);
|
TfLiteTensor *output = this->streaming_interpreter_->output(0);
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ namespace mitsubishi {
|
|||||||
|
|
||||||
static const char *const TAG = "mitsubishi.climate";
|
static const char *const TAG = "mitsubishi.climate";
|
||||||
|
|
||||||
const uint32_t MITSUBISHI_OFF = 0x00;
|
const uint8_t MITSUBISHI_OFF = 0x00;
|
||||||
|
|
||||||
const uint8_t MITSUBISHI_MODE_AUTO = 0x20;
|
const uint8_t MITSUBISHI_MODE_AUTO = 0x20;
|
||||||
const uint8_t MITSUBISHI_MODE_COOL = 0x18;
|
const uint8_t MITSUBISHI_MODE_COOL = 0x18;
|
||||||
@ -109,8 +109,8 @@ void MitsubishiClimate::transmit_state() {
|
|||||||
// Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00
|
// Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00
|
||||||
// Byte 16: Constant 0x00
|
// Byte 16: Constant 0x00
|
||||||
// Byte 17: Checksum: SUM[Byte0...Byte16]
|
// Byte 17: Checksum: SUM[Byte0...Byte16]
|
||||||
uint32_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x00, 0x00,
|
uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
switch (this->mode) {
|
switch (this->mode) {
|
||||||
case climate::CLIMATE_MODE_HEAT:
|
case climate::CLIMATE_MODE_HEAT:
|
||||||
@ -249,7 +249,7 @@ void MitsubishiClimate::transmit_state() {
|
|||||||
|
|
||||||
data->set_carrier_frequency(38000);
|
data->set_carrier_frequency(38000);
|
||||||
// repeat twice
|
// repeat twice
|
||||||
for (uint16_t r = 0; r < 2; r++) {
|
for (uint8_t r = 0; r < 2; r++) {
|
||||||
// Header
|
// Header
|
||||||
data->mark(MITSUBISHI_HEADER_MARK);
|
data->mark(MITSUBISHI_HEADER_MARK);
|
||||||
data->space(MITSUBISHI_HEADER_SPACE);
|
data->space(MITSUBISHI_HEADER_SPACE);
|
||||||
|
@ -12,6 +12,7 @@ from esphome.const import (
|
|||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
|
UNIT_MILLIMETER,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
CONF_BATTERY_LEVEL,
|
CONF_BATTERY_LEVEL,
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
@ -25,8 +26,6 @@ ICON_PROPANE_TANK = "mdi:propane-tank"
|
|||||||
|
|
||||||
TANK_TYPE_CUSTOM = "CUSTOM"
|
TANK_TYPE_CUSTOM = "CUSTOM"
|
||||||
|
|
||||||
UNIT_MILLIMETER = "mm"
|
|
||||||
|
|
||||||
|
|
||||||
def small_distance(value):
|
def small_distance(value):
|
||||||
"""small_distance is stored in mm"""
|
"""small_distance is stored in mm"""
|
||||||
|
@ -12,6 +12,7 @@ from esphome.const import (
|
|||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
|
UNIT_MILLIMETER,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
CONF_BATTERY_LEVEL,
|
CONF_BATTERY_LEVEL,
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
@ -26,8 +27,6 @@ ICON_PROPANE_TANK = "mdi:propane-tank"
|
|||||||
|
|
||||||
TANK_TYPE_CUSTOM = "CUSTOM"
|
TANK_TYPE_CUSTOM = "CUSTOM"
|
||||||
|
|
||||||
UNIT_MILLIMETER = "mm"
|
|
||||||
|
|
||||||
|
|
||||||
def small_distance(value):
|
def small_distance(value):
|
||||||
"""small_distance is stored in mm"""
|
"""small_distance is stored in mm"""
|
||||||
|
@ -100,7 +100,7 @@ def process_calibration(value):
|
|||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
if len(value) != 3:
|
if len(value) != 3:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Steinhart–Hart Calibration must consist of exactly three values"
|
"Steinhart-Hart Calibration must consist of exactly three values"
|
||||||
)
|
)
|
||||||
value = cv.Schema([validate_calibration_parameter])(value)
|
value = cv.Schema([validate_calibration_parameter])(value)
|
||||||
a, b, c = calc_steinhart_hart(value)
|
a, b, c = calc_steinhart_hart(value)
|
||||||
|
@ -2,6 +2,7 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt
|
||||||
|
from esphome.components import web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ABOVE,
|
CONF_ABOVE,
|
||||||
CONF_BELOW,
|
CONF_BELOW,
|
||||||
@ -18,6 +19,7 @@ from esphome.const import (
|
|||||||
CONF_VALUE,
|
CONF_VALUE,
|
||||||
CONF_OPERATION,
|
CONF_OPERATION,
|
||||||
CONF_CYCLE,
|
CONF_CYCLE,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
DEVICE_CLASS_APPARENT_POWER,
|
DEVICE_CLASS_APPARENT_POWER,
|
||||||
DEVICE_CLASS_AQI,
|
DEVICE_CLASS_AQI,
|
||||||
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
||||||
@ -167,26 +169,30 @@ NUMBER_OPERATION_OPTIONS = {
|
|||||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||||
validate_unit_of_measurement = cv.string_strict
|
validate_unit_of_measurement = cv.string_strict
|
||||||
|
|
||||||
NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
NUMBER_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(NumberStateTrigger),
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
|
||||||
}
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(NumberStateTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger),
|
),
|
||||||
cv.Optional(CONF_ABOVE): cv.templatable(cv.float_),
|
cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation(
|
||||||
cv.Optional(CONF_BELOW): cv.templatable(cv.float_),
|
{
|
||||||
},
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger),
|
||||||
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
|
cv.Optional(CONF_ABOVE): cv.templatable(cv.float_),
|
||||||
),
|
cv.Optional(CONF_BELOW): cv.templatable(cv.float_),
|
||||||
cv.Optional(CONF_UNIT_OF_MEASUREMENT): validate_unit_of_measurement,
|
},
|
||||||
cv.Optional(CONF_MODE, default="AUTO"): cv.enum(NUMBER_MODES, upper=True),
|
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
|
||||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
),
|
||||||
}
|
cv.Optional(CONF_UNIT_OF_MEASUREMENT): validate_unit_of_measurement,
|
||||||
|
cv.Optional(CONF_MODE, default="AUTO"): cv.enum(NUMBER_MODES, upper=True),
|
||||||
|
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
@ -248,6 +254,10 @@ async def setup_number_core_(
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_number(
|
async def register_number(
|
||||||
var, config, *, min_value: float, max_value: float, step: float
|
var, config, *, min_value: float, max_value: float, step: float
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "remote_base.h"
|
#include "remote_base.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cinttypes>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -144,7 +145,8 @@ class ABBWelcomeData {
|
|||||||
std::string to_string(uint8_t max_print_bytes = 255) const {
|
std::string to_string(uint8_t max_print_bytes = 255) const {
|
||||||
std::string info;
|
std::string info;
|
||||||
if (this->is_valid()) {
|
if (this->is_valid()) {
|
||||||
info = str_sprintf(this->get_three_byte_address() ? "[%06X %s %06X] Type: %02X" : "[%04X %s %04X] Type: %02X",
|
info = str_sprintf(this->get_three_byte_address() ? "[%06" PRIX32 " %s %06" PRIX32 "] Type: %02X"
|
||||||
|
: "[%04" PRIX32 " %s %04" PRIX32 "] Type: %02X",
|
||||||
this->get_source_address(), this->get_retransmission() ? "»" : ">",
|
this->get_source_address(), this->get_retransmission() ? "»" : ">",
|
||||||
this->get_destination_address(), this->get_message_type());
|
this->get_destination_address(), this->get_message_type());
|
||||||
if (this->get_data_size())
|
if (this->get_data_size())
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "byronsx_protocol.h"
|
#include "byronsx_protocol.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace remote_base {
|
namespace remote_base {
|
||||||
|
|
||||||
@ -57,7 +59,7 @@ void ByronSXProtocol::encode(RemoteTransmitData *dst, const ByronSXData &data) {
|
|||||||
out_data <<= NBITS_COMMAND;
|
out_data <<= NBITS_COMMAND;
|
||||||
out_data |= data.command;
|
out_data |= data.command;
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Send ByronSX: out_data %03x", out_data);
|
ESP_LOGV(TAG, "Send ByronSX: out_data %03" PRIx32, out_data);
|
||||||
|
|
||||||
// Initial Mark start bit
|
// Initial Mark start bit
|
||||||
dst->mark(1 * BIT_TIME_US);
|
dst->mark(1 * BIT_TIME_US);
|
||||||
@ -90,13 +92,16 @@ optional<ByronSXData> ByronSXProtocol::decode(RemoteReceiveData src) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGVV(TAG, "%3d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", src.size(), src.peek(0),
|
ESP_LOGVV(TAG,
|
||||||
src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), src.peek(7), src.peek(8),
|
"%3" PRId32 ": %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
|
||||||
src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14), src.peek(15),
|
" %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
|
||||||
src.peek(16), src.peek(17), src.peek(18), src.peek(19));
|
" %" PRId32 " %" PRId32 " %" PRId32,
|
||||||
|
src.size(), src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6),
|
||||||
|
src.peek(7), src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14),
|
||||||
|
src.peek(15), src.peek(16), src.peek(17), src.peek(18), src.peek(19));
|
||||||
|
|
||||||
ESP_LOGVV(TAG, " %d %d %d %d %d %d", src.peek(20), src.peek(21), src.peek(22), src.peek(23), src.peek(24),
|
ESP_LOGVV(TAG, " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32, src.peek(20),
|
||||||
src.peek(25));
|
src.peek(21), src.peek(22), src.peek(23), src.peek(24), src.peek(25));
|
||||||
|
|
||||||
// Read data bits
|
// Read data bits
|
||||||
uint32_t out_data = 0;
|
uint32_t out_data = 0;
|
||||||
@ -107,10 +112,10 @@ optional<ByronSXData> ByronSXProtocol::decode(RemoteReceiveData src) {
|
|||||||
} else if (src.expect_space(BIT_TIME_US) && src.expect_mark(2 * BIT_TIME_US)) {
|
} else if (src.expect_space(BIT_TIME_US) && src.expect_mark(2 * BIT_TIME_US)) {
|
||||||
out_data |= 0 << bit;
|
out_data |= 0 << bit;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Decode ByronSX: Fail 2, %2d %08x", bit, out_data);
|
ESP_LOGV(TAG, "Decode ByronSX: Fail 2, %2d %08" PRIx32, bit, out_data);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
ESP_LOGVV(TAG, "Decode ByronSX: Data, %2d %08x", bit, out_data);
|
ESP_LOGVV(TAG, "Decode ByronSX: Data, %2d %08" PRIx32, bit, out_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// last bit followed by a long space
|
// last bit followed by a long space
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "drayton_protocol.h"
|
#include "drayton_protocol.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace remote_base {
|
namespace remote_base {
|
||||||
|
|
||||||
@ -151,12 +153,12 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
|
|||||||
|
|
||||||
// Look for sync pulse, after. If sucessful index points to space of sync symbol
|
// Look for sync pulse, after. If sucessful index points to space of sync symbol
|
||||||
while (src.size() - src.get_index() >= MIN_RX_SRC) {
|
while (src.size() - src.get_index() >= MIN_RX_SRC) {
|
||||||
ESP_LOGVV(TAG, "Decode Drayton: sync search %d, %" PRId32 " %" PRId32, src.size() - src.get_index(), src.peek(),
|
ESP_LOGVV(TAG, "Decode Drayton: sync search %" PRIu32 ", %" PRId32 " %" PRId32, src.size() - src.get_index(),
|
||||||
src.peek(1));
|
src.peek(), src.peek(1));
|
||||||
if (src.peek_mark(2 * BIT_TIME_US) &&
|
if (src.peek_mark(2 * BIT_TIME_US) &&
|
||||||
(src.peek_space(2 * BIT_TIME_US, 1) || src.peek_space(3 * BIT_TIME_US, 1))) {
|
(src.peek_space(2 * BIT_TIME_US, 1) || src.peek_space(3 * BIT_TIME_US, 1))) {
|
||||||
src.advance(1);
|
src.advance(1);
|
||||||
ESP_LOGVV(TAG, "Decode Drayton: Found SYNC, - %d", src.get_index());
|
ESP_LOGVV(TAG, "Decode Drayton: Found SYNC, - %" PRIu32, src.get_index());
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
src.advance(2);
|
src.advance(2);
|
||||||
@ -174,14 +176,16 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
|
|||||||
// Checks next bit to leave index pointing correctly
|
// Checks next bit to leave index pointing correctly
|
||||||
uint32_t out_data = 0;
|
uint32_t out_data = 0;
|
||||||
uint8_t bit = NDATABITS - 1;
|
uint8_t bit = NDATABITS - 1;
|
||||||
ESP_LOGVV(TAG, "Decode Drayton: first bit %d %" PRId32 ", %" PRId32, src.peek(0), src.peek(1), src.peek(2));
|
ESP_LOGVV(TAG, "Decode Drayton: first bit %" PRId32 " %" PRId32 ", %" PRId32, src.peek(0), src.peek(1),
|
||||||
|
src.peek(2));
|
||||||
if (src.expect_space(3 * BIT_TIME_US) && (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) {
|
if (src.expect_space(3 * BIT_TIME_US) && (src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) {
|
||||||
out_data |= 0 << bit;
|
out_data |= 0 << bit;
|
||||||
} else if (src.expect_space(2 * BIT_TIME_US) && src.expect_mark(BIT_TIME_US) &&
|
} else if (src.expect_space(2 * BIT_TIME_US) && src.expect_mark(BIT_TIME_US) &&
|
||||||
(src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) {
|
(src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) {
|
||||||
out_data |= 1 << bit;
|
out_data |= 1 << bit;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Decode Drayton: Fail 2, - %d %d %d", src.peek(-1), src.peek(0), src.peek(1));
|
ESP_LOGV(TAG, "Decode Drayton: Fail 2, - %" PRId32 " %" PRId32 " %" PRId32, src.peek(-1), src.peek(0),
|
||||||
|
src.peek(1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +206,8 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bit > 0) {
|
if (bit > 0) {
|
||||||
ESP_LOGVV(TAG, "Decode Drayton: Fail 3, %d %" PRId32 " %" PRId32, src.peek(-1), src.peek(0), src.peek(1));
|
ESP_LOGVV(TAG, "Decode Drayton: Fail 3, %" PRId32 " %" PRId32 " %" PRId32, src.peek(-1), src.peek(0),
|
||||||
|
src.peek(1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +219,7 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Decode Drayton: Data, %2d %08x", bit, out_data);
|
ESP_LOGV(TAG, "Decode Drayton: Data, %2d %08" PRIx32, bit, out_data);
|
||||||
|
|
||||||
out.channel = (uint8_t) (out_data & 0x1F);
|
out.channel = (uint8_t) (out_data & 0x1F);
|
||||||
out_data >>= NBITS_CHANNEL;
|
out_data >>= NBITS_CHANNEL;
|
||||||
|
@ -52,7 +52,7 @@ void KeeloqProtocol::encode(RemoteTransmitData *dst, const KeeloqData &data) {
|
|||||||
// Encrypted field
|
// Encrypted field
|
||||||
out_data = data.encrypted;
|
out_data = data.encrypted;
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Send Keeloq: Encrypted data %04x", out_data);
|
ESP_LOGV(TAG, "Send Keeloq: Encrypted data %04" PRIx32, out_data);
|
||||||
|
|
||||||
for (uint32_t mask = 1, cnt = 0; cnt < NBITS_ENCRYPTED_DATA; cnt++, mask <<= 1) {
|
for (uint32_t mask = 1, cnt = 0; cnt < NBITS_ENCRYPTED_DATA; cnt++, mask <<= 1) {
|
||||||
if (out_data & mask) {
|
if (out_data & mask) {
|
||||||
@ -68,7 +68,7 @@ void KeeloqProtocol::encode(RemoteTransmitData *dst, const KeeloqData &data) {
|
|||||||
out_data = (data.command & 0x0f);
|
out_data = (data.command & 0x0f);
|
||||||
out_data <<= NBITS_SERIAL;
|
out_data <<= NBITS_SERIAL;
|
||||||
out_data |= data.address;
|
out_data |= data.address;
|
||||||
ESP_LOGV(TAG, "Send Keeloq: Fixed data %04x", out_data);
|
ESP_LOGV(TAG, "Send Keeloq: Fixed data %04" PRIx32, out_data);
|
||||||
|
|
||||||
for (uint32_t mask = 1, cnt = 0; cnt < (NBITS_FIXED_DATA - 2); cnt++, mask <<= 1) {
|
for (uint32_t mask = 1, cnt = 0; cnt < (NBITS_FIXED_DATA - 2); cnt++, mask <<= 1) {
|
||||||
if (out_data & mask) {
|
if (out_data & mask) {
|
||||||
@ -111,21 +111,24 @@ optional<KeeloqData> KeeloqProtocol::decode(RemoteReceiveData src) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGVV(TAG, "%2d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", src.size(), src.peek(0),
|
ESP_LOGVV(TAG,
|
||||||
src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), src.peek(7), src.peek(8),
|
"%2" PRId32 ": %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
|
||||||
src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14), src.peek(15),
|
" %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
|
||||||
src.peek(16), src.peek(17), src.peek(18), src.peek(19));
|
" %" PRId32 " %" PRId32 " %" PRId32,
|
||||||
|
src.size(), src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6),
|
||||||
|
src.peek(7), src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14),
|
||||||
|
src.peek(15), src.peek(16), src.peek(17), src.peek(18), src.peek(19));
|
||||||
|
|
||||||
// Check preamble bits
|
// Check preamble bits
|
||||||
int8_t bit = NBITS_PREAMBLE - 1;
|
int8_t bit = NBITS_PREAMBLE - 1;
|
||||||
while (--bit >= 0) {
|
while (--bit >= 0) {
|
||||||
if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(BIT_TIME_US)) {
|
if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(BIT_TIME_US)) {
|
||||||
ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %d", bit + 1, src.peek());
|
ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %" PRId32, bit + 1, src.peek());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(10 * BIT_TIME_US)) {
|
if (!src.expect_mark(BIT_TIME_US) || !src.expect_space(10 * BIT_TIME_US)) {
|
||||||
ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %d", bit + 1, src.peek());
|
ESP_LOGV(TAG, "Decode KeeLoq: Fail 1, %d %" PRId32, bit + 1, src.peek());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,11 +140,11 @@ optional<KeeloqData> KeeloqProtocol::decode(RemoteReceiveData src) {
|
|||||||
} else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
|
} else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
|
||||||
out_data |= 1 << bit;
|
out_data |= 1 << bit;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Decode KeeLoq: Fail 2, %d %d", src.get_index(), src.peek());
|
ESP_LOGV(TAG, "Decode KeeLoq: Fail 2, %" PRIu32 " %" PRId32, src.get_index(), src.peek());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ESP_LOGVV(TAG, "Decode KeeLoq: Data, %d %08x", bit, out_data);
|
ESP_LOGVV(TAG, "Decode KeeLoq: Data, %d %08" PRIx32, bit, out_data);
|
||||||
out.encrypted = out_data;
|
out.encrypted = out_data;
|
||||||
|
|
||||||
// Read Serial Number and Button Status
|
// Read Serial Number and Button Status
|
||||||
@ -152,11 +155,11 @@ optional<KeeloqData> KeeloqProtocol::decode(RemoteReceiveData src) {
|
|||||||
} else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
|
} else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
|
||||||
out_data |= 1 << bit;
|
out_data |= 1 << bit;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Decode KeeLoq: Fail 3, %d %d", src.get_index(), src.peek());
|
ESP_LOGV(TAG, "Decode KeeLoq: Fail 3, %" PRIu32 " %" PRId32, src.get_index(), src.peek());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ESP_LOGVV(TAG, "Decode KeeLoq: Data, %2d %08x", bit, out_data);
|
ESP_LOGVV(TAG, "Decode KeeLoq: Data, %2d %08" PRIx32, bit, out_data);
|
||||||
out.command = (out_data >> 28) & 0xf;
|
out.command = (out_data >> 28) & 0xf;
|
||||||
out.address = out_data & 0xfffffff;
|
out.address = out_data & 0xfffffff;
|
||||||
|
|
||||||
@ -166,7 +169,7 @@ optional<KeeloqData> KeeloqProtocol::decode(RemoteReceiveData src) {
|
|||||||
} else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
|
} else if (src.expect_mark(BIT_TIME_US) && src.expect_space(2 * BIT_TIME_US)) {
|
||||||
out.vlow = true;
|
out.vlow = true;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Decode KeeLoq: Fail 4, %08x", src.peek());
|
ESP_LOGV(TAG, "Decode KeeLoq: Fail 4, %" PRId32, src.peek());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +179,7 @@ optional<KeeloqData> KeeloqProtocol::decode(RemoteReceiveData src) {
|
|||||||
} else if (src.expect_mark(BIT_TIME_US) && src.peek_space_at_least(2 * BIT_TIME_US)) {
|
} else if (src.expect_mark(BIT_TIME_US) && src.peek_space_at_least(2 * BIT_TIME_US)) {
|
||||||
out.repeat = true;
|
out.repeat = true;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGV(TAG, "Decode KeeLoq: Fail 5, %08x", src.peek());
|
ESP_LOGV(TAG, "Decode KeeLoq: Fail 5, %" PRId32, src.peek());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ENTITY_CATEGORY,
|
CONF_ENTITY_CATEGORY,
|
||||||
CONF_ICON,
|
CONF_ICON,
|
||||||
@ -10,6 +10,7 @@ from esphome.const import (
|
|||||||
CONF_OPTION,
|
CONF_OPTION,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_CYCLE,
|
CONF_CYCLE,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
CONF_OPERATION,
|
CONF_OPERATION,
|
||||||
@ -47,16 +48,20 @@ SELECT_OPERATION_OPTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
SELECT_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSelectComponent),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(Select),
|
.extend(
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
{
|
||||||
{
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSelectComponent),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SelectStateTrigger),
|
cv.GenerateID(): cv.declare_id(Select),
|
||||||
}
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
}
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SelectStateTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
@ -99,6 +104,10 @@ async def setup_select_core_(var, config, *, options: list[str]):
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_select(var, config, *, options: list[str]):
|
async def register_select(var, config, *, options: list[str]):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -3,7 +3,7 @@ import math
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_ABOVE,
|
CONF_ABOVE,
|
||||||
@ -31,6 +31,7 @@ from esphome.const import (
|
|||||||
CONF_UNIT_OF_MEASUREMENT,
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
CONF_WINDOW_SIZE,
|
CONF_WINDOW_SIZE,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_FORCE_UPDATE,
|
CONF_FORCE_UPDATE,
|
||||||
CONF_VALUE,
|
CONF_VALUE,
|
||||||
CONF_MIN_VALUE,
|
CONF_MIN_VALUE,
|
||||||
@ -252,43 +253,49 @@ validate_accuracy_decimals = cv.int_
|
|||||||
validate_icon = cv.icon
|
validate_icon = cv.icon
|
||||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||||
|
|
||||||
SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
SENSOR_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSensorComponent),
|
.extend(cv.MQTT_COMPONENT_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(Sensor),
|
.extend(
|
||||||
cv.Optional(CONF_UNIT_OF_MEASUREMENT): validate_unit_of_measurement,
|
{
|
||||||
cv.Optional(CONF_ACCURACY_DECIMALS): validate_accuracy_decimals,
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSensorComponent),
|
||||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
cv.GenerateID(): cv.declare_id(Sensor),
|
||||||
cv.Optional(CONF_STATE_CLASS): validate_state_class,
|
cv.Optional(CONF_UNIT_OF_MEASUREMENT): validate_unit_of_measurement,
|
||||||
cv.Optional(CONF_ENTITY_CATEGORY): sensor_entity_category,
|
cv.Optional(CONF_ACCURACY_DECIMALS): validate_accuracy_decimals,
|
||||||
cv.Optional("last_reset_type"): cv.invalid(
|
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||||
"last_reset_type has been removed since 2021.9.0. state_class: total_increasing should be used for total values."
|
cv.Optional(CONF_STATE_CLASS): validate_state_class,
|
||||||
),
|
cv.Optional(CONF_ENTITY_CATEGORY): sensor_entity_category,
|
||||||
cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean,
|
cv.Optional("last_reset_type"): cv.invalid(
|
||||||
cv.Optional(CONF_EXPIRE_AFTER): cv.All(
|
"last_reset_type has been removed since 2021.9.0. state_class: total_increasing should be used for total values."
|
||||||
cv.requires_component("mqtt"),
|
),
|
||||||
cv.Any(None, cv.positive_time_period_milliseconds),
|
cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean,
|
||||||
),
|
cv.Optional(CONF_EXPIRE_AFTER): cv.All(
|
||||||
cv.Optional(CONF_FILTERS): validate_filters,
|
cv.requires_component("mqtt"),
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
cv.Any(None, cv.positive_time_period_milliseconds),
|
||||||
{
|
),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorStateTrigger),
|
cv.Optional(CONF_FILTERS): validate_filters,
|
||||||
}
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorStateTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorRawStateTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
{
|
SensorRawStateTrigger
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger),
|
),
|
||||||
cv.Optional(CONF_ABOVE): cv.templatable(cv.float_),
|
}
|
||||||
cv.Optional(CONF_BELOW): cv.templatable(cv.float_),
|
),
|
||||||
},
|
cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation(
|
||||||
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
|
{
|
||||||
),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger),
|
||||||
}
|
cv.Optional(CONF_ABOVE): cv.templatable(cv.float_),
|
||||||
|
cv.Optional(CONF_BELOW): cv.templatable(cv.float_),
|
||||||
|
},
|
||||||
|
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
@ -772,6 +779,10 @@ async def setup_sensor_core_(var, config):
|
|||||||
else:
|
else:
|
||||||
cg.add(mqtt_.set_expire_after(expire_after))
|
cg.add(mqtt_.set_expire_after(expire_after))
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_sensor(var, config):
|
async def register_sensor(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
#include "sntp_component.h"
|
#include "sntp_component.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
|
||||||
#include "lwip/apps/sntp.h"
|
|
||||||
#ifdef USE_ESP_IDF
|
#ifdef USE_ESP_IDF
|
||||||
#include "esp_sntp.h"
|
#include "esp_sntp.h"
|
||||||
#endif
|
#elif USE_ESP8266
|
||||||
#endif
|
|
||||||
#ifdef USE_ESP8266
|
|
||||||
#include "sntp.h"
|
#include "sntp.h"
|
||||||
#endif
|
#else
|
||||||
#ifdef USE_RP2040
|
|
||||||
#include "lwip/apps/sntp.h"
|
#include "lwip/apps/sntp.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -27,14 +22,14 @@ static const char *const TAG = "sntp";
|
|||||||
void SNTPComponent::setup() {
|
void SNTPComponent::setup() {
|
||||||
#ifndef USE_HOST
|
#ifndef USE_HOST
|
||||||
ESP_LOGCONFIG(TAG, "Setting up SNTP...");
|
ESP_LOGCONFIG(TAG, "Setting up SNTP...");
|
||||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
#if defined(USE_ESP_IDF)
|
||||||
if (sntp_enabled()) {
|
if (esp_sntp_enabled()) {
|
||||||
sntp_stop();
|
esp_sntp_stop();
|
||||||
}
|
}
|
||||||
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
|
||||||
#endif
|
#else
|
||||||
#ifdef USE_ESP8266
|
|
||||||
sntp_stop();
|
sntp_stop();
|
||||||
|
sntp_setoperatingmode(SNTP_OPMODE_POLL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sntp_setservername(0, strdup(this->server_1_.c_str()));
|
sntp_setservername(0, strdup(this->server_1_.c_str()));
|
||||||
@ -45,7 +40,7 @@ void SNTPComponent::setup() {
|
|||||||
sntp_setservername(2, strdup(this->server_3_.c_str()));
|
sntp_setservername(2, strdup(this->server_3_.c_str()));
|
||||||
}
|
}
|
||||||
#ifdef USE_ESP_IDF
|
#ifdef USE_ESP_IDF
|
||||||
sntp_set_sync_interval(this->get_update_interval());
|
esp_sntp_set_sync_interval(this->get_update_interval());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
sntp_init();
|
sntp_init();
|
||||||
|
@ -469,7 +469,8 @@ class LWIPRawImpl : public Socket {
|
|||||||
}
|
}
|
||||||
ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override {
|
ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override {
|
||||||
// return ::sendto(fd_, buf, len, flags, to, tolen);
|
// return ::sendto(fd_, buf, len, flags, to, tolen);
|
||||||
return 0;
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
int setblocking(bool blocking) override {
|
int setblocking(bool blocking) override {
|
||||||
if (pcb_ == nullptr) {
|
if (pcb_ == nullptr) {
|
||||||
|
@ -128,7 +128,8 @@ bool SonoffD1Output::read_ack_(const uint8_t *cmd, const size_t len) {
|
|||||||
// Expected acknowledgement from rf chip
|
// Expected acknowledgement from rf chip
|
||||||
uint8_t ref_buffer[7] = {0xAA, 0x55, cmd[2], cmd[3], 0x00, 0x00, 0x00};
|
uint8_t ref_buffer[7] = {0xAA, 0x55, cmd[2], cmd[3], 0x00, 0x00, 0x00};
|
||||||
uint8_t buffer[sizeof(ref_buffer)] = {0};
|
uint8_t buffer[sizeof(ref_buffer)] = {0};
|
||||||
uint32_t pos = 0, buf_len = sizeof(ref_buffer);
|
uint32_t pos = 0;
|
||||||
|
size_t buf_len = sizeof(ref_buffer);
|
||||||
|
|
||||||
// Update the reference checksum
|
// Update the reference checksum
|
||||||
this->populate_checksum_(ref_buffer, sizeof(ref_buffer));
|
this->populate_checksum_(ref_buffer, sizeof(ref_buffer));
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition, maybe_simple_id
|
from esphome.automation import Condition, maybe_simple_id
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_ENTITY_CATEGORY,
|
CONF_ENTITY_CATEGORY,
|
||||||
@ -10,6 +10,7 @@ from esphome.const import (
|
|||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_INVERTED,
|
CONF_INVERTED,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_ON_TURN_OFF,
|
CONF_ON_TURN_OFF,
|
||||||
CONF_ON_TURN_ON,
|
CONF_ON_TURN_ON,
|
||||||
CONF_RESTORE_MODE,
|
CONF_RESTORE_MODE,
|
||||||
@ -64,22 +65,26 @@ SwitchTurnOffTrigger = switch_ns.class_(
|
|||||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True)
|
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True)
|
||||||
|
|
||||||
|
|
||||||
_SWITCH_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
_SWITCH_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.Optional(CONF_INVERTED): cv.boolean,
|
.extend(
|
||||||
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
|
{
|
||||||
{
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger),
|
cv.Optional(CONF_INVERTED): cv.boolean,
|
||||||
}
|
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOffTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOffTrigger),
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
@ -151,6 +156,10 @@ async def setup_switch_core_(var, config):
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
||||||
cg.add(var.set_device_class(device_class))
|
cg.add(var.set_device_class(device_class))
|
||||||
|
|
||||||
|
@ -2,13 +2,14 @@ from typing import Optional
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
CONF_ON_VALUE,
|
CONF_ON_VALUE,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_VALUE,
|
CONF_VALUE,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,17 +39,21 @@ TEXT_MODES = {
|
|||||||
"PASSWORD": TextMode.TEXT_MODE_PASSWORD, # to be implemented for keys, passwords, etc.
|
"PASSWORD": TextMode.TEXT_MODE_PASSWORD, # to be implemented for keys, passwords, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
TEXT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
TEXT_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextComponent),
|
.extend(cv.MQTT_COMPONENT_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(Text),
|
.extend(
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
{
|
||||||
{
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextComponent),
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TextStateTrigger),
|
cv.GenerateID(): cv.declare_id(Text),
|
||||||
}
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Required(CONF_MODE): cv.enum(TEXT_MODES, upper=True),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TextStateTrigger),
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
cv.Required(CONF_MODE): cv.enum(TEXT_MODES, upper=True),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -77,6 +82,10 @@ async def setup_text_core_(
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_text(
|
async def register_text(
|
||||||
var,
|
var,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_ENTITY_CATEGORY,
|
CONF_ENTITY_CATEGORY,
|
||||||
@ -12,6 +12,7 @@ from esphome.const import (
|
|||||||
CONF_ON_RAW_VALUE,
|
CONF_ON_RAW_VALUE,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
CONF_STATE,
|
CONF_STATE,
|
||||||
CONF_FROM,
|
CONF_FROM,
|
||||||
CONF_TO,
|
CONF_TO,
|
||||||
@ -124,25 +125,31 @@ async def map_filter_to_code(config, filter_id):
|
|||||||
|
|
||||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||||
|
|
||||||
TEXT_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
TEXT_SENSOR_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor),
|
.extend(cv.MQTT_COMPONENT_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(TextSensor),
|
.extend(
|
||||||
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
{
|
||||||
cv.Optional(CONF_FILTERS): validate_filters,
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor),
|
||||||
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
cv.GenerateID(): cv.declare_id(TextSensor),
|
||||||
{
|
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TextSensorStateTrigger),
|
cv.Optional(CONF_FILTERS): validate_filters,
|
||||||
}
|
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
{
|
TextSensorStateTrigger
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
),
|
||||||
TextSensorStateRawTrigger
|
}
|
||||||
),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation(
|
||||||
),
|
{
|
||||||
}
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
|
TextSensorStateRawTrigger
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
@ -205,6 +212,10 @@ async def setup_text_sensor_core_(var, config):
|
|||||||
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_text_sensor(var, config):
|
async def register_text_sensor(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -269,6 +269,30 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff
|
|||||||
ESP_LOGV(TAG, "Network status requested, reported as %i", wifi_status);
|
ESP_LOGV(TAG, "Network status requested, reported as %i", wifi_status);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TuyaCommandType::EXTENDED_SERVICES: {
|
||||||
|
uint8_t subcommand = buffer[0];
|
||||||
|
switch ((TuyaExtendedServicesCommandType) subcommand) {
|
||||||
|
case TuyaExtendedServicesCommandType::RESET_NOTIFICATION: {
|
||||||
|
this->send_command_(
|
||||||
|
TuyaCommand{.cmd = TuyaCommandType::EXTENDED_SERVICES,
|
||||||
|
.payload = std::vector<uint8_t>{
|
||||||
|
static_cast<uint8_t>(TuyaExtendedServicesCommandType::RESET_NOTIFICATION), 0x00}});
|
||||||
|
ESP_LOGV(TAG, "Reset status notification enabled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TuyaExtendedServicesCommandType::MODULE_RESET: {
|
||||||
|
ESP_LOGE(TAG, "EXTENDED_SERVICES::MODULE_RESET is not handled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TuyaExtendedServicesCommandType::UPDATE_IN_PROGRESS: {
|
||||||
|
ESP_LOGE(TAG, "EXTENDED_SERVICES::UPDATE_IN_PROGRESS is not handled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Invalid extended services subcommand (0x%02X) received", subcommand);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
ESP_LOGE(TAG, "Invalid command (0x%02X) received", command);
|
ESP_LOGE(TAG, "Invalid command (0x%02X) received", command);
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,13 @@ enum class TuyaCommandType : uint8_t {
|
|||||||
WIFI_RSSI = 0x24,
|
WIFI_RSSI = 0x24,
|
||||||
VACUUM_MAP_UPLOAD = 0x28,
|
VACUUM_MAP_UPLOAD = 0x28,
|
||||||
GET_NETWORK_STATUS = 0x2B,
|
GET_NETWORK_STATUS = 0x2B,
|
||||||
|
EXTENDED_SERVICES = 0x34,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TuyaExtendedServicesCommandType : uint8_t {
|
||||||
|
RESET_NOTIFICATION = 0x04,
|
||||||
|
MODULE_RESET = 0x05,
|
||||||
|
UPDATE_IN_PROGRESS = 0x0A,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TuyaInitState : uint8_t {
|
enum class TuyaInitState : uint8_t {
|
||||||
|
@ -69,7 +69,7 @@ void IDFUARTComponent::setup() {
|
|||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->uart_num_ = next_uart_num++;
|
this->uart_num_ = static_cast<uart_port_t>(next_uart_num++);
|
||||||
ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_);
|
ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_);
|
||||||
|
|
||||||
this->lock_ = xSemaphoreCreateMutex();
|
this->lock_ = xSemaphoreCreateMutex();
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import maybe_simple_id, Condition
|
from esphome.automation import maybe_simple_id, Condition
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, web_server
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
@ -14,6 +14,7 @@ from esphome.const import (
|
|||||||
CONF_STATE,
|
CONF_STATE,
|
||||||
CONF_STOP,
|
CONF_STOP,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
DEVICE_CLASS_EMPTY,
|
DEVICE_CLASS_EMPTY,
|
||||||
DEVICE_CLASS_GAS,
|
DEVICE_CLASS_GAS,
|
||||||
DEVICE_CLASS_WATER,
|
DEVICE_CLASS_WATER,
|
||||||
@ -70,28 +71,32 @@ ValveClosedTrigger = valve_ns.class_(
|
|||||||
|
|
||||||
CONF_ON_CLOSED = "on_closed"
|
CONF_ON_CLOSED = "on_closed"
|
||||||
|
|
||||||
VALVE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
VALVE_SCHEMA = (
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
cv.GenerateID(): cv.declare_id(Valve),
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTValveComponent),
|
.extend(
|
||||||
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
|
{
|
||||||
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
|
cv.GenerateID(): cv.declare_id(Valve),
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTValveComponent),
|
||||||
),
|
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
|
||||||
cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
|
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
|
||||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
|
cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
|
||||||
{
|
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValveOpenTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
|
||||||
),
|
{
|
||||||
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValveOpenTrigger),
|
||||||
{
|
}
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValveClosedTrigger),
|
),
|
||||||
}
|
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
|
||||||
),
|
{
|
||||||
}
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValveClosedTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -119,6 +124,10 @@ async def setup_valve_core_(var, config):
|
|||||||
mqtt_.set_custom_position_command_topic(position_command_topic_config)
|
mqtt_.set_custom_position_command_topic(position_command_topic_config)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None:
|
||||||
|
web_server_ = await cg.get_variable(webserver_id)
|
||||||
|
web_server.add_entity_to_sorting_list(web_server_, var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_valve(var, config):
|
async def register_valve(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
|||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ACTUAL_GAIN,
|
CONF_ACTUAL_GAIN,
|
||||||
|
CONF_AMBIENT_LIGHT,
|
||||||
CONF_AUTO_MODE,
|
CONF_AUTO_MODE,
|
||||||
CONF_FULL_SPECTRUM,
|
CONF_FULL_SPECTRUM,
|
||||||
CONF_GAIN,
|
CONF_GAIN,
|
||||||
@ -11,13 +12,13 @@ from esphome.const import (
|
|||||||
CONF_INFRARED,
|
CONF_INFRARED,
|
||||||
CONF_INTEGRATION_TIME,
|
CONF_INTEGRATION_TIME,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
UNIT_LUX,
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
UNIT_MILLISECOND,
|
|
||||||
ICON_BRIGHTNESS_5,
|
ICON_BRIGHTNESS_5,
|
||||||
ICON_BRIGHTNESS_6,
|
ICON_BRIGHTNESS_6,
|
||||||
ICON_TIMER,
|
ICON_TIMER,
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_LUX,
|
||||||
|
UNIT_MILLISECOND,
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@latonita"]
|
CODEOWNERS = ["@latonita"]
|
||||||
@ -28,7 +29,6 @@ ICON_MULTIPLICATION = "mdi:multiplication"
|
|||||||
ICON_BRIGHTNESS_7 = "mdi:brightness-7"
|
ICON_BRIGHTNESS_7 = "mdi:brightness-7"
|
||||||
|
|
||||||
CONF_ACTUAL_INTEGRATION_TIME = "actual_integration_time"
|
CONF_ACTUAL_INTEGRATION_TIME = "actual_integration_time"
|
||||||
CONF_AMBIENT_LIGHT = "ambient_light"
|
|
||||||
CONF_AMBIENT_LIGHT_COUNTS = "ambient_light_counts"
|
CONF_AMBIENT_LIGHT_COUNTS = "ambient_light_counts"
|
||||||
CONF_FULL_SPECTRUM_COUNTS = "full_spectrum_counts"
|
CONF_FULL_SPECTRUM_COUNTS = "full_spectrum_counts"
|
||||||
CONF_LUX_COMPENSATION = "lux_compensation"
|
CONF_LUX_COMPENSATION = "lux_compensation"
|
||||||
|
@ -44,6 +44,12 @@ CONF_VOLUME_MULTIPLIER = "volume_multiplier"
|
|||||||
|
|
||||||
CONF_WAKE_WORD = "wake_word"
|
CONF_WAKE_WORD = "wake_word"
|
||||||
|
|
||||||
|
CONF_ON_TIMER_STARTED = "on_timer_started"
|
||||||
|
CONF_ON_TIMER_UPDATED = "on_timer_updated"
|
||||||
|
CONF_ON_TIMER_CANCELLED = "on_timer_cancelled"
|
||||||
|
CONF_ON_TIMER_FINISHED = "on_timer_finished"
|
||||||
|
CONF_ON_TIMER_TICK = "on_timer_tick"
|
||||||
|
|
||||||
|
|
||||||
voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant")
|
voice_assistant_ns = cg.esphome_ns.namespace("voice_assistant")
|
||||||
VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component)
|
VoiceAssistant = voice_assistant_ns.class_("VoiceAssistant", cg.Component)
|
||||||
@ -64,6 +70,8 @@ ConnectedCondition = voice_assistant_ns.class_(
|
|||||||
"ConnectedCondition", automation.Condition, cg.Parented.template(VoiceAssistant)
|
"ConnectedCondition", automation.Condition, cg.Parented.template(VoiceAssistant)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Timer = voice_assistant_ns.struct("Timer")
|
||||||
|
|
||||||
|
|
||||||
def tts_stream_validate(config):
|
def tts_stream_validate(config):
|
||||||
if CONF_SPEAKER not in config and (
|
if CONF_SPEAKER not in config and (
|
||||||
@ -131,6 +139,21 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
single=True
|
single=True
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_IDLE): automation.validate_automation(single=True),
|
cv.Optional(CONF_ON_IDLE): automation.validate_automation(single=True),
|
||||||
|
cv.Optional(CONF_ON_TIMER_STARTED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_UPDATED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_CANCELLED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_FINISHED): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TIMER_TICK): automation.validate_automation(
|
||||||
|
single=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
tts_stream_validate,
|
tts_stream_validate,
|
||||||
@ -270,6 +293,49 @@ async def to_code(config):
|
|||||||
config[CONF_ON_IDLE],
|
config[CONF_ON_IDLE],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
has_timers = False
|
||||||
|
if on_timer_started := config.get(CONF_ON_TIMER_STARTED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_started_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_started,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_updated := config.get(CONF_ON_TIMER_UPDATED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_updated_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_updated,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_cancelled := config.get(CONF_ON_TIMER_CANCELLED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_cancelled_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_cancelled,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_finished := config.get(CONF_ON_TIMER_FINISHED):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_finished_trigger(),
|
||||||
|
[(Timer, "timer")],
|
||||||
|
on_timer_finished,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
if on_timer_tick := config.get(CONF_ON_TIMER_TICK):
|
||||||
|
await automation.build_automation(
|
||||||
|
var.get_timer_tick_trigger(),
|
||||||
|
[(cg.std_vector.template(Timer), "timers")],
|
||||||
|
on_timer_tick,
|
||||||
|
)
|
||||||
|
has_timers = True
|
||||||
|
|
||||||
|
cg.add(var.set_has_timers(has_timers))
|
||||||
|
|
||||||
cg.add_define("USE_VOICE_ASSISTANT")
|
cg.add_define("USE_VOICE_ASSISTANT")
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@ -17,7 +18,7 @@ static const char *const TAG = "voice_assistant";
|
|||||||
|
|
||||||
static const size_t SAMPLE_RATE_HZ = 16000;
|
static const size_t SAMPLE_RATE_HZ = 16000;
|
||||||
static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms
|
static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms
|
||||||
static const size_t BUFFER_SIZE = 1024 * SAMPLE_RATE_HZ / 1000;
|
static const size_t BUFFER_SIZE = 512 * SAMPLE_RATE_HZ / 1000;
|
||||||
static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t);
|
static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t);
|
||||||
static const size_t RECEIVE_SIZE = 1024;
|
static const size_t RECEIVE_SIZE = 1024;
|
||||||
static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE;
|
static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE;
|
||||||
@ -622,7 +623,7 @@ void VoiceAssistant::signal_stop_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
||||||
ESP_LOGD(TAG, "Event Type: %d", msg.event_type);
|
ESP_LOGD(TAG, "Event Type: %" PRId32, msg.event_type);
|
||||||
switch (msg.event_type) {
|
switch (msg.event_type) {
|
||||||
case api::enums::VOICE_ASSISTANT_RUN_START:
|
case api::enums::VOICE_ASSISTANT_RUN_START:
|
||||||
ESP_LOGD(TAG, "Assist Pipeline running");
|
ESP_LOGD(TAG, "Assist Pipeline running");
|
||||||
@ -785,7 +786,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
|||||||
this->defer([this]() { this->stt_vad_end_trigger_->trigger(); });
|
this->defer([this]() { this->stt_vad_end_trigger_->trigger(); });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ESP_LOGD(TAG, "Unhandled event type: %d", msg.event_type);
|
ESP_LOGD(TAG, "Unhandled event type: %" PRId32, msg.event_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -797,12 +798,65 @@ void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) {
|
|||||||
this->speaker_buffer_index_ += msg.data.length();
|
this->speaker_buffer_index_ += msg.data.length();
|
||||||
this->speaker_buffer_size_ += msg.data.length();
|
this->speaker_buffer_size_ += msg.data.length();
|
||||||
this->speaker_bytes_received_ += msg.data.length();
|
this->speaker_bytes_received_ += msg.data.length();
|
||||||
|
ESP_LOGD(TAG, "Received audio: %d bytes from API", msg.data.length());
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "Cannot receive audio, buffer is full");
|
ESP_LOGE(TAG, "Cannot receive audio, buffer is full");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoiceAssistant::on_timer_event(const api::VoiceAssistantTimerEventResponse &msg) {
|
||||||
|
Timer timer = {
|
||||||
|
.id = msg.timer_id,
|
||||||
|
.name = msg.name,
|
||||||
|
.total_seconds = msg.total_seconds,
|
||||||
|
.seconds_left = msg.seconds_left,
|
||||||
|
.is_active = msg.is_active,
|
||||||
|
};
|
||||||
|
this->timers_[timer.id] = timer;
|
||||||
|
ESP_LOGD(TAG, "Timer Event");
|
||||||
|
ESP_LOGD(TAG, " Type: %" PRId32, msg.event_type);
|
||||||
|
ESP_LOGD(TAG, " %s", timer.to_string().c_str());
|
||||||
|
|
||||||
|
switch (msg.event_type) {
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_STARTED:
|
||||||
|
this->timer_started_trigger_->trigger(timer);
|
||||||
|
break;
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_UPDATED:
|
||||||
|
this->timer_updated_trigger_->trigger(timer);
|
||||||
|
break;
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_CANCELLED:
|
||||||
|
this->timer_cancelled_trigger_->trigger(timer);
|
||||||
|
this->timers_.erase(timer.id);
|
||||||
|
break;
|
||||||
|
case api::enums::VOICE_ASSISTANT_TIMER_FINISHED:
|
||||||
|
this->timer_finished_trigger_->trigger(timer);
|
||||||
|
this->timers_.erase(timer.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->timers_.empty()) {
|
||||||
|
this->cancel_interval("timer-event");
|
||||||
|
this->timer_tick_running_ = false;
|
||||||
|
} else if (!this->timer_tick_running_) {
|
||||||
|
this->set_interval("timer-event", 1000, [this]() { this->timer_tick_(); });
|
||||||
|
this->timer_tick_running_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoiceAssistant::timer_tick_() {
|
||||||
|
std::vector<Timer> res;
|
||||||
|
res.reserve(this->timers_.size());
|
||||||
|
for (auto &pair : this->timers_) {
|
||||||
|
auto &timer = pair.second;
|
||||||
|
if (timer.is_active && timer.seconds_left > 0) {
|
||||||
|
timer.seconds_left--;
|
||||||
|
}
|
||||||
|
res.push_back(timer);
|
||||||
|
}
|
||||||
|
this->timer_tick_trigger_->trigger(res);
|
||||||
|
}
|
||||||
|
|
||||||
VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
} // namespace voice_assistant
|
} // namespace voice_assistant
|
||||||
|
@ -24,6 +24,9 @@
|
|||||||
#include <esp_vad.h>
|
#include <esp_vad.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace voice_assistant {
|
namespace voice_assistant {
|
||||||
|
|
||||||
@ -36,6 +39,7 @@ enum VoiceAssistantFeature : uint32_t {
|
|||||||
FEATURE_VOICE_ASSISTANT = 1 << 0,
|
FEATURE_VOICE_ASSISTANT = 1 << 0,
|
||||||
FEATURE_SPEAKER = 1 << 1,
|
FEATURE_SPEAKER = 1 << 1,
|
||||||
FEATURE_API_AUDIO = 1 << 2,
|
FEATURE_API_AUDIO = 1 << 2,
|
||||||
|
FEATURE_TIMERS = 1 << 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
@ -59,6 +63,20 @@ enum AudioMode : uint8_t {
|
|||||||
AUDIO_MODE_API,
|
AUDIO_MODE_API,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Timer {
|
||||||
|
std::string id;
|
||||||
|
std::string name;
|
||||||
|
uint32_t total_seconds;
|
||||||
|
uint32_t seconds_left;
|
||||||
|
bool is_active;
|
||||||
|
|
||||||
|
std::string to_string() const {
|
||||||
|
return str_sprintf("Timer(id=%s, name=%s, total_seconds=%" PRIu32 ", seconds_left=%" PRIu32 ", is_active=%s)",
|
||||||
|
this->id.c_str(), this->name.c_str(), this->total_seconds, this->seconds_left,
|
||||||
|
YESNO(this->is_active));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class VoiceAssistant : public Component {
|
class VoiceAssistant : public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
@ -100,6 +118,11 @@ class VoiceAssistant : public Component {
|
|||||||
flags |= VoiceAssistantFeature::FEATURE_SPEAKER;
|
flags |= VoiceAssistantFeature::FEATURE_SPEAKER;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (this->has_timers_) {
|
||||||
|
flags |= VoiceAssistantFeature::FEATURE_TIMERS;
|
||||||
|
}
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +131,7 @@ class VoiceAssistant : public Component {
|
|||||||
|
|
||||||
void on_event(const api::VoiceAssistantEventResponse &msg);
|
void on_event(const api::VoiceAssistantEventResponse &msg);
|
||||||
void on_audio(const api::VoiceAssistantAudio &msg);
|
void on_audio(const api::VoiceAssistantAudio &msg);
|
||||||
|
void on_timer_event(const api::VoiceAssistantTimerEventResponse &msg);
|
||||||
|
|
||||||
bool is_running() const { return this->state_ != State::IDLE; }
|
bool is_running() const { return this->state_ != State::IDLE; }
|
||||||
void set_continuous(bool continuous) { this->continuous_ = continuous; }
|
void set_continuous(bool continuous) { this->continuous_ = continuous; }
|
||||||
@ -150,6 +174,14 @@ class VoiceAssistant : public Component {
|
|||||||
|
|
||||||
void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; }
|
void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; }
|
||||||
|
|
||||||
|
Trigger<Timer> *get_timer_started_trigger() const { return this->timer_started_trigger_; }
|
||||||
|
Trigger<Timer> *get_timer_updated_trigger() const { return this->timer_updated_trigger_; }
|
||||||
|
Trigger<Timer> *get_timer_cancelled_trigger() const { return this->timer_cancelled_trigger_; }
|
||||||
|
Trigger<Timer> *get_timer_finished_trigger() const { return this->timer_finished_trigger_; }
|
||||||
|
Trigger<std::vector<Timer>> *get_timer_tick_trigger() const { return this->timer_tick_trigger_; }
|
||||||
|
void set_has_timers(bool has_timers) { this->has_timers_ = has_timers; }
|
||||||
|
const std::unordered_map<std::string, Timer> &get_timers() const { return this->timers_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool allocate_buffers_();
|
bool allocate_buffers_();
|
||||||
void clear_buffers_();
|
void clear_buffers_();
|
||||||
@ -186,6 +218,16 @@ class VoiceAssistant : public Component {
|
|||||||
|
|
||||||
api::APIConnection *api_client_{nullptr};
|
api::APIConnection *api_client_{nullptr};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Timer> timers_;
|
||||||
|
void timer_tick_();
|
||||||
|
Trigger<Timer> *timer_started_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<Timer> *timer_finished_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<Timer> *timer_updated_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<Timer> *timer_cancelled_trigger_ = new Trigger<Timer>();
|
||||||
|
Trigger<std::vector<Timer>> *timer_tick_trigger_ = new Trigger<std::vector<Timer>>();
|
||||||
|
bool has_timers_{false};
|
||||||
|
bool timer_tick_running_{false};
|
||||||
|
|
||||||
microphone::Microphone *mic_{nullptr};
|
microphone::Microphone *mic_{nullptr};
|
||||||
#ifdef USE_SPEAKER
|
#ifdef USE_SPEAKER
|
||||||
void write_speaker_();
|
void write_speaker_();
|
||||||
|
@ -1 +1 @@
|
|||||||
CODEOWNERS = ["@willwill2will54"]
|
CODEOWNERS = ["@willwill2will54", "@clydebarrow"]
|
||||||
|
@ -2,6 +2,16 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import button
|
from esphome.components import button
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
from esphome.core import CORE
|
||||||
|
|
||||||
|
DEPENDENCIES = ["network"]
|
||||||
|
|
||||||
|
|
||||||
|
def AUTO_LOAD():
|
||||||
|
if CORE.is_esp8266 or CORE.is_rp2040:
|
||||||
|
return []
|
||||||
|
return ["socket"]
|
||||||
|
|
||||||
|
|
||||||
CONF_TARGET_MAC_ADDRESS = "target_mac_address"
|
CONF_TARGET_MAC_ADDRESS = "target_mac_address"
|
||||||
|
|
||||||
@ -9,25 +19,19 @@ wake_on_lan_ns = cg.esphome_ns.namespace("wake_on_lan")
|
|||||||
|
|
||||||
WakeOnLanButton = wake_on_lan_ns.class_("WakeOnLanButton", button.Button, cg.Component)
|
WakeOnLanButton = wake_on_lan_ns.class_("WakeOnLanButton", button.Button, cg.Component)
|
||||||
|
|
||||||
DEPENDENCIES = ["network"]
|
CONFIG_SCHEMA = (
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
|
||||||
button.button_schema(WakeOnLanButton)
|
button.button_schema(WakeOnLanButton)
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
.extend(
|
.extend(
|
||||||
cv.Schema(
|
{
|
||||||
{
|
cv.Required(CONF_TARGET_MAC_ADDRESS): cv.mac_address,
|
||||||
cv.Required(CONF_TARGET_MAC_ADDRESS): cv.mac_address,
|
}
|
||||||
}
|
)
|
||||||
),
|
|
||||||
),
|
|
||||||
cv.only_with_arduino,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
cg.add(var.set_macaddr(*config[CONF_TARGET_MAC_ADDRESS].parts))
|
||||||
yield cg.add(var.set_macaddr(*config[CONF_TARGET_MAC_ADDRESS].parts))
|
await cg.register_component(var, config)
|
||||||
yield cg.register_component(var, config)
|
await button.register_button(var, config)
|
||||||
yield button.register_button(var, config)
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#ifdef USE_ARDUINO
|
|
||||||
|
|
||||||
#include "wake_on_lan.h"
|
#include "wake_on_lan.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/components/network/ip_address.h"
|
#include "esphome/components/network/ip_address.h"
|
||||||
@ -22,40 +20,68 @@ void WakeOnLanButton::set_macaddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, ui
|
|||||||
|
|
||||||
void WakeOnLanButton::dump_config() {
|
void WakeOnLanButton::dump_config() {
|
||||||
LOG_BUTTON("", "Wake-on-LAN Button", this);
|
LOG_BUTTON("", "Wake-on-LAN Button", this);
|
||||||
ESP_LOGCONFIG(TAG, " Target MAC address: %02X:%02X:%02X:%02X:%02X:%02X", macaddr_[0], macaddr_[1], macaddr_[2],
|
ESP_LOGCONFIG(TAG, " Target MAC address: %02X:%02X:%02X:%02X:%02X:%02X", this->macaddr_[0], this->macaddr_[1],
|
||||||
macaddr_[3], macaddr_[4], macaddr_[5]);
|
this->macaddr_[2], this->macaddr_[3], this->macaddr_[4], this->macaddr_[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WakeOnLanButton::press_action() {
|
void WakeOnLanButton::press_action() {
|
||||||
|
if (!network::is_connected()) {
|
||||||
|
ESP_LOGW(TAG, "Network not connected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
ESP_LOGI(TAG, "Sending Wake-on-LAN Packet...");
|
ESP_LOGI(TAG, "Sending Wake-on-LAN Packet...");
|
||||||
bool begin_status = false;
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
bool end_status = false;
|
struct sockaddr_storage saddr {};
|
||||||
|
auto addr_len =
|
||||||
|
socket::set_sockaddr(reinterpret_cast<sockaddr *>(&saddr), sizeof(saddr), "255.255.255.255", this->port_);
|
||||||
|
uint8_t buffer[6 + sizeof this->macaddr_ * 16];
|
||||||
|
memcpy(buffer, PREFIX, sizeof(PREFIX));
|
||||||
|
for (size_t i = 0; i != 16; i++) {
|
||||||
|
memcpy(buffer + i * sizeof(this->macaddr_) + sizeof(PREFIX), this->macaddr_, sizeof(this->macaddr_));
|
||||||
|
}
|
||||||
|
if (this->broadcast_socket_->sendto(buffer, sizeof(buffer), 0, reinterpret_cast<const sockaddr *>(&saddr),
|
||||||
|
addr_len) <= 0)
|
||||||
|
ESP_LOGW(TAG, "sendto() error %d", errno);
|
||||||
|
#else
|
||||||
IPAddress broadcast = IPAddress(255, 255, 255, 255);
|
IPAddress broadcast = IPAddress(255, 255, 255, 255);
|
||||||
#ifdef USE_ESP8266
|
|
||||||
for (auto ip : esphome::network::get_ip_addresses()) {
|
for (auto ip : esphome::network::get_ip_addresses()) {
|
||||||
if (ip.is_ip4()) {
|
if (ip.is_ip4()) {
|
||||||
begin_status = this->udp_client_.beginPacketMulticast(broadcast, 9, ip, 128);
|
if (this->udp_client_.beginPacketMulticast(broadcast, 9, ip, 128) != 0) {
|
||||||
break;
|
this->udp_client_.write(PREFIX, 6);
|
||||||
|
for (size_t i = 0; i < 16; i++) {
|
||||||
|
this->udp_client_.write(macaddr_, 6);
|
||||||
|
}
|
||||||
|
if (this->udp_client_.endPacket() != 0)
|
||||||
|
return;
|
||||||
|
ESP_LOGW(TAG, "WOL broadcast failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ESP_LOGW(TAG, "No ip4 addresses to broadcast to");
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32
|
}
|
||||||
begin_status = this->udp_client_.beginPacket(broadcast, 9);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (begin_status) {
|
void WakeOnLanButton::setup() {
|
||||||
this->udp_client_.write(PREFIX, 6);
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
for (size_t i = 0; i < 16; i++) {
|
this->broadcast_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||||
this->udp_client_.write(macaddr_, 6);
|
if (this->broadcast_socket_ == nullptr) {
|
||||||
}
|
this->mark_failed();
|
||||||
end_status = this->udp_client_.endPacket();
|
this->status_set_error("Could not create socket");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!begin_status || end_status) {
|
int enable = 1;
|
||||||
ESP_LOGE(TAG, "Sending Wake-on-LAN Packet Failed!");
|
auto err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||||
|
if (err != 0) {
|
||||||
|
this->status_set_warning("Socket unable to set reuseaddr");
|
||||||
|
// we can still continue
|
||||||
}
|
}
|
||||||
|
err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int));
|
||||||
|
if (err != 0) {
|
||||||
|
this->status_set_warning("Socket unable to set broadcast");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace wake_on_lan
|
} // namespace wake_on_lan
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
|
|
||||||
#include "esphome/components/button/button.h"
|
#include "esphome/components/button/button.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
|
#include "esphome/components/socket/socket.h"
|
||||||
|
#else
|
||||||
#include "WiFiUdp.h"
|
#include "WiFiUdp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace wake_on_lan {
|
namespace wake_on_lan {
|
||||||
@ -14,14 +16,19 @@ class WakeOnLanButton : public button::Button, public Component {
|
|||||||
void set_macaddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f);
|
void set_macaddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f);
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
void setup() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
|
||||||
|
std::unique_ptr<socket::Socket> broadcast_socket_{};
|
||||||
|
#else
|
||||||
WiFiUDP udp_client_{};
|
WiFiUDP udp_client_{};
|
||||||
|
#endif
|
||||||
void press_action() override;
|
void press_action() override;
|
||||||
|
uint16_t port_{9};
|
||||||
uint8_t macaddr_[6];
|
uint8_t macaddr_[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace wake_on_lan
|
} // namespace wake_on_lan
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -48,7 +48,7 @@ WaveshareEPaper2P9InBV3 = waveshare_epaper_ns.class_(
|
|||||||
WaveshareEPaper2P9InV2R2 = waveshare_epaper_ns.class_(
|
WaveshareEPaper2P9InV2R2 = waveshare_epaper_ns.class_(
|
||||||
"WaveshareEPaper2P9InV2R2", WaveshareEPaper
|
"WaveshareEPaper2P9InV2R2", WaveshareEPaper
|
||||||
)
|
)
|
||||||
GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper)
|
GDEW029T5 = waveshare_epaper_ns.class_("GDEW029T5", WaveshareEPaper)
|
||||||
WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_(
|
WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_(
|
||||||
"WaveshareEPaper2P9InDKE", WaveshareEPaper
|
"WaveshareEPaper2P9InDKE", WaveshareEPaper
|
||||||
)
|
)
|
||||||
@ -110,7 +110,7 @@ MODELS = {
|
|||||||
"2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74),
|
"2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74),
|
||||||
"2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
|
"2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
|
||||||
"2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2),
|
"2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2),
|
||||||
"gdey029t94": ("c", GDEY029T94),
|
"gdew029t5": ("c", GDEW029T5),
|
||||||
"2.70in": ("b", WaveshareEPaper2P7In),
|
"2.70in": ("b", WaveshareEPaper2P7In),
|
||||||
"2.70in-b": ("b", WaveshareEPaper2P7InB),
|
"2.70in-b": ("b", WaveshareEPaper2P7InB),
|
||||||
"2.70in-bv2": ("b", WaveshareEPaper2P7InBV2),
|
"2.70in-bv2": ("b", WaveshareEPaper2P7InBV2),
|
||||||
|
@ -1514,7 +1514,7 @@ void WaveshareEPaper2P9InV2R2::set_full_update_every(uint32_t full_update_every)
|
|||||||
// - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h
|
// - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h
|
||||||
// ========================================================
|
// ========================================================
|
||||||
|
|
||||||
void GDEY029T94::initialize() {
|
void GDEW029T5::initialize() {
|
||||||
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37
|
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37
|
||||||
// EPD hardware init start
|
// EPD hardware init start
|
||||||
this->reset_();
|
this->reset_();
|
||||||
@ -1560,7 +1560,7 @@ void GDEY029T94::initialize() {
|
|||||||
|
|
||||||
// EPD hardware init end
|
// EPD hardware init end
|
||||||
}
|
}
|
||||||
void HOT GDEY029T94::display() {
|
void HOT GDEW029T5::display() {
|
||||||
// COMMAND DATA START TRANSMISSION 2 (B/W only)
|
// COMMAND DATA START TRANSMISSION 2 (B/W only)
|
||||||
this->command(0x13);
|
this->command(0x13);
|
||||||
delay(2);
|
delay(2);
|
||||||
@ -1580,11 +1580,11 @@ void HOT GDEY029T94::display() {
|
|||||||
// NOTE: power off < deep sleep
|
// NOTE: power off < deep sleep
|
||||||
this->command(0x02);
|
this->command(0x02);
|
||||||
}
|
}
|
||||||
int GDEY029T94::get_width_internal() { return 128; }
|
int GDEW029T5::get_width_internal() { return 128; }
|
||||||
int GDEY029T94::get_height_internal() { return 296; }
|
int GDEW029T5::get_height_internal() { return 296; }
|
||||||
void GDEY029T94::dump_config() {
|
void GDEW029T5::dump_config() {
|
||||||
LOG_DISPLAY("", "Waveshare E-Paper (Good Display)", this);
|
LOG_DISPLAY("", "Waveshare E-Paper (Good Display)", this);
|
||||||
ESP_LOGCONFIG(TAG, " Model: 2.9in Greyscale GDEY029T94");
|
ESP_LOGCONFIG(TAG, " Model: 2.9in Greyscale GDEW029T5");
|
||||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||||
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
LOG_PIN(" Busy Pin: ", this->busy_pin_);
|
||||||
|
@ -227,7 +227,7 @@ class WaveshareEPaper2P7InBV2 : public WaveshareEPaperBWR {
|
|||||||
int get_height_internal() override;
|
int get_height_internal() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GDEY029T94 : public WaveshareEPaper {
|
class GDEW029T5 : public WaveshareEPaper {
|
||||||
public:
|
public:
|
||||||
void initialize() override;
|
void initialize() override;
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import gzip
|
import gzip
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
import esphome.final_validate as fv
|
||||||
from esphome.components import web_server_base
|
from esphome.components import web_server_base
|
||||||
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
|
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
@ -19,6 +22,8 @@ from esphome.const import (
|
|||||||
CONF_LOG,
|
CONF_LOG,
|
||||||
CONF_VERSION,
|
CONF_VERSION,
|
||||||
CONF_LOCAL,
|
CONF_LOCAL,
|
||||||
|
CONF_WEB_SERVER_ID,
|
||||||
|
CONF_WEB_SERVER_SORTING_WEIGHT,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PLATFORM_BK72XX,
|
PLATFORM_BK72XX,
|
||||||
@ -64,6 +69,46 @@ def validate_ota(config):
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_no_sorting_weight(
|
||||||
|
webserver_version: int, config: dict, path: list[str] | None = None
|
||||||
|
) -> None:
|
||||||
|
if path is None:
|
||||||
|
path = []
|
||||||
|
if CONF_WEB_SERVER_SORTING_WEIGHT in config:
|
||||||
|
raise cv.FinalExternalInvalid(
|
||||||
|
f"Sorting weight on entities is not supported in web_server version {webserver_version}",
|
||||||
|
path=path + [CONF_WEB_SERVER_SORTING_WEIGHT],
|
||||||
|
)
|
||||||
|
for p, value in config.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
_validate_no_sorting_weight(webserver_version, value, path + [p])
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for i, item in enumerate(value):
|
||||||
|
if isinstance(item, dict):
|
||||||
|
_validate_no_sorting_weight(webserver_version, item, path + [p, i])
|
||||||
|
|
||||||
|
|
||||||
|
def _final_validate_sorting_weight(config):
|
||||||
|
if (webserver_version := config.get(CONF_VERSION)) != 3:
|
||||||
|
_validate_no_sorting_weight(webserver_version, fv.full_config.get())
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = _final_validate_sorting_weight
|
||||||
|
|
||||||
|
|
||||||
|
WEBSERVER_SORTING_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer),
|
||||||
|
cv.Optional(CONF_WEB_SERVER_SORTING_WEIGHT): cv.All(
|
||||||
|
cv.requires_component("web_server"),
|
||||||
|
cv.float_,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
@ -108,6 +153,19 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def add_entity_to_sorting_list(web_server, entity, config):
|
||||||
|
sorting_weight = 50
|
||||||
|
if CONF_WEB_SERVER_SORTING_WEIGHT in config:
|
||||||
|
sorting_weight = config[CONF_WEB_SERVER_SORTING_WEIGHT]
|
||||||
|
|
||||||
|
cg.add(
|
||||||
|
web_server.add_entity_to_sorting_list(
|
||||||
|
entity,
|
||||||
|
sorting_weight,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_index_html(config) -> str:
|
def build_index_html(config) -> str:
|
||||||
html = "<!DOCTYPE html><html><head><meta charset=UTF-8><link rel=icon href=data:>"
|
html = "<!DOCTYPE html><html><head><meta charset=UTF-8><link rel=icon href=data:>"
|
||||||
css_include = config.get(CONF_CSS_INCLUDE)
|
css_include = config.get(CONF_CSS_INCLUDE)
|
||||||
|
@ -435,7 +435,7 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
|
std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
std::string state;
|
std::string state;
|
||||||
if (std::isnan(value)) {
|
if (std::isnan(value)) {
|
||||||
state = "NA";
|
state = "NA";
|
||||||
@ -446,6 +446,9 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
|
|||||||
}
|
}
|
||||||
set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
|
set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
if (!obj->get_unit_of_measurement().empty())
|
if (!obj->get_unit_of_measurement().empty())
|
||||||
root["uom"] = obj->get_unit_of_measurement();
|
root["uom"] = obj->get_unit_of_measurement();
|
||||||
}
|
}
|
||||||
@ -471,8 +474,13 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const
|
|||||||
}
|
}
|
||||||
std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
|
std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
|
||||||
JsonDetail start_config) {
|
JsonDetail start_config) {
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
|
set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -483,14 +491,6 @@ void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
|
|||||||
return;
|
return;
|
||||||
this->events_.send(this->switch_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->switch_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
|
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
|
||||||
set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
|
|
||||||
if (start_config == DETAIL_ALL) {
|
|
||||||
root["assumed_state"] = obj->assumed_state();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
for (switch_::Switch *obj : App.get_switches()) {
|
for (switch_::Switch *obj : App.get_switches()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
@ -515,14 +515,20 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
|
|||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
|
||||||
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
|
set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
root["assumed_state"] = obj->assumed_state();
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
|
|
||||||
return json::build_json(
|
|
||||||
[obj, start_config](JsonObject root) { set_json_id(root, obj, "button-" + obj->get_object_id(), start_config); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
for (button::Button *obj : App.get_buttons()) {
|
for (button::Button *obj : App.get_buttons()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
@ -538,6 +544,16 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
|
|||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
|
||||||
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
|
set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
@ -546,12 +562,6 @@ void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s
|
|||||||
return;
|
return;
|
||||||
this->events_.send(this->binary_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->binary_sensor_json(obj, state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
|
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
|
||||||
set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
|
|
||||||
start_config);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
|
for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
@ -562,6 +572,17 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
|
|||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
|
||||||
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
|
set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
|
||||||
|
start_config);
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
@ -570,19 +591,6 @@ void WebServer::on_fan_update(fan::Fan *obj) {
|
|||||||
return;
|
return;
|
||||||
this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->fan_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
|
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
|
||||||
set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
|
|
||||||
start_config);
|
|
||||||
const auto traits = obj->get_traits();
|
|
||||||
if (traits.supports_speed()) {
|
|
||||||
root["speed_level"] = obj->speed;
|
|
||||||
root["speed_count"] = traits.supported_speed_count();
|
|
||||||
}
|
|
||||||
if (obj->get_traits().supports_oscillation())
|
|
||||||
root["oscillation"] = obj->oscillating;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
for (fan::Fan *obj : App.get_fans()) {
|
for (fan::Fan *obj : App.get_fans()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
@ -635,6 +643,24 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
|||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
|
||||||
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
|
set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
|
||||||
|
start_config);
|
||||||
|
const auto traits = obj->get_traits();
|
||||||
|
if (traits.supports_speed()) {
|
||||||
|
root["speed_level"] = obj->speed;
|
||||||
|
root["speed_count"] = traits.supported_speed_count();
|
||||||
|
}
|
||||||
|
if (obj->get_traits().supports_oscillation())
|
||||||
|
root["oscillation"] = obj->oscillating;
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
@ -729,7 +755,7 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
|
std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
|
||||||
root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
|
root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
|
||||||
|
|
||||||
@ -740,6 +766,9 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
|
|||||||
for (auto const &option : obj->get_effects()) {
|
for (auto const &option : obj->get_effects()) {
|
||||||
opt.add(option->get_name());
|
opt.add(option->get_name());
|
||||||
}
|
}
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -803,7 +832,7 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
|
set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
|
||||||
obj->position, start_config);
|
obj->position, start_config);
|
||||||
root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
|
root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
|
||||||
@ -812,6 +841,11 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
|||||||
root["position"] = obj->position;
|
root["position"] = obj->position;
|
||||||
if (obj->get_traits().get_supports_tilt())
|
if (obj->get_traits().get_supports_tilt())
|
||||||
root["tilt"] = obj->tilt;
|
root["tilt"] = obj->tilt;
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -852,7 +886,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
|
std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
root["min_value"] =
|
root["min_value"] =
|
||||||
@ -864,6 +898,9 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
|||||||
root["mode"] = (int) obj->traits.get_mode();
|
root["mode"] = (int) obj->traits.get_mode();
|
||||||
if (!obj->traits.get_unit_of_measurement().empty())
|
if (!obj->traits.get_unit_of_measurement().empty())
|
||||||
root["uom"] = obj->traits.get_unit_of_measurement();
|
root["uom"] = obj->traits.get_unit_of_measurement();
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (std::isnan(value)) {
|
if (std::isnan(value)) {
|
||||||
root["value"] = "\"NaN\"";
|
root["value"] = "\"NaN\"";
|
||||||
@ -919,11 +956,16 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
|
std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
|
||||||
std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
|
std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
root["state"] = value;
|
root["state"] = value;
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif // USE_DATETIME_DATE
|
#endif // USE_DATETIME_DATE
|
||||||
@ -967,11 +1009,16 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
|
std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
|
||||||
std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
|
std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
root["state"] = value;
|
root["state"] = value;
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif // USE_DATETIME_TIME
|
#endif // USE_DATETIME_TIME
|
||||||
@ -1015,12 +1062,17 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) {
|
std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
|
||||||
std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour,
|
std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour,
|
||||||
obj->minute, obj->second);
|
obj->minute, obj->second);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
root["state"] = value;
|
root["state"] = value;
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif // USE_DATETIME_DATETIME
|
#endif // USE_DATETIME_DATETIME
|
||||||
@ -1060,11 +1112,8 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
|
std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
|
||||||
root["mode"] = (int) obj->traits.get_mode();
|
|
||||||
}
|
|
||||||
root["min_length"] = obj->traits.get_min_length();
|
root["min_length"] = obj->traits.get_min_length();
|
||||||
root["max_length"] = obj->traits.get_max_length();
|
root["max_length"] = obj->traits.get_max_length();
|
||||||
root["pattern"] = obj->traits.get_pattern();
|
root["pattern"] = obj->traits.get_pattern();
|
||||||
@ -1074,6 +1123,12 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
|
|||||||
root["state"] = value;
|
root["state"] = value;
|
||||||
}
|
}
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
root["mode"] = (int) obj->traits.get_mode();
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1119,13 +1174,16 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
|
std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
|
set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray opt = root.createNestedArray("option");
|
JsonArray opt = root.createNestedArray("option");
|
||||||
for (auto &option : obj->traits.get_options()) {
|
for (auto &option : obj->traits.get_options()) {
|
||||||
opt.add(option);
|
opt.add(option);
|
||||||
}
|
}
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1140,7 +1198,6 @@ void WebServer::on_climate_update(climate::Climate *obj) {
|
|||||||
return;
|
return;
|
||||||
this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
for (auto *obj : App.get_climates()) {
|
for (auto *obj : App.get_climates()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
@ -1188,9 +1245,8 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
|
|||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
|
std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
|
||||||
const auto traits = obj->get_traits();
|
const auto traits = obj->get_traits();
|
||||||
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
|
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
|
||||||
@ -1227,6 +1283,9 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
|||||||
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
||||||
opt.add(custom_preset);
|
opt.add(custom_preset);
|
||||||
}
|
}
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_state = false;
|
bool has_state = false;
|
||||||
@ -1283,12 +1342,6 @@ void WebServer::on_lock_update(lock::Lock *obj) {
|
|||||||
return;
|
return;
|
||||||
this->events_.send(this->lock_json(obj, obj->state, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->lock_json(obj, obj->state, DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
|
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
|
||||||
set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
|
|
||||||
start_config);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
for (lock::Lock *obj : App.get_locks()) {
|
for (lock::Lock *obj : App.get_locks()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
@ -1313,6 +1366,17 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
|
|||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
|
||||||
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
|
set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
|
||||||
|
start_config);
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
@ -1366,13 +1430,16 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
|
|||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
|
std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
|
||||||
return json::build_json([obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
|
set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
|
||||||
obj->position, start_config);
|
obj->position, start_config);
|
||||||
root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
|
root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
|
||||||
|
|
||||||
if (obj->get_traits().get_supports_position())
|
if (obj->get_traits().get_supports_position())
|
||||||
root["position"] = obj->position;
|
root["position"] = obj->position;
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1383,15 +1450,6 @@ void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP
|
|||||||
return;
|
return;
|
||||||
this->events_.send(this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE).c_str(), "state");
|
||||||
}
|
}
|
||||||
std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
|
|
||||||
alarm_control_panel::AlarmControlPanelState value,
|
|
||||||
JsonDetail start_config) {
|
|
||||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
|
||||||
char buf[16];
|
|
||||||
set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
|
|
||||||
PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||||
for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) {
|
for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) {
|
||||||
if (obj->get_object_id() != match.id)
|
if (obj->get_object_id() != match.id)
|
||||||
@ -1405,6 +1463,20 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
|
|||||||
}
|
}
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
|
||||||
|
alarm_control_panel::AlarmControlPanelState value,
|
||||||
|
JsonDetail start_config) {
|
||||||
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
|
char buf[16];
|
||||||
|
set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
|
||||||
|
PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
|
||||||
|
if (start_config == DETAIL_ALL) {
|
||||||
|
if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
|
||||||
|
root["sorting_weight"] = this->sorting_entitys_[obj].weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
@ -1709,6 +1781,10 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
|||||||
|
|
||||||
bool WebServer::isRequestHandlerTrivial() { return false; }
|
bool WebServer::isRequestHandlerTrivial() { return false; }
|
||||||
|
|
||||||
|
void WebServer::add_entity_to_sorting_list(EntityBase *entity, float weight) {
|
||||||
|
this->sorting_entitys_[entity] = SortingComponents{weight};
|
||||||
|
}
|
||||||
|
|
||||||
void WebServer::schedule_(std::function<void()> &&f) {
|
void WebServer::schedule_(std::function<void()> &&f) {
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);
|
xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
#include "esphome/components/web_server_base/web_server_base.h"
|
#include "esphome/components/web_server_base/web_server_base.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/controller.h"
|
#include "esphome/core/controller.h"
|
||||||
|
#include "esphome/core/entity_base.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
@ -39,6 +41,10 @@ struct UrlMatch {
|
|||||||
bool valid; ///< Whether this match is valid
|
bool valid; ///< Whether this match is valid
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SortingComponents {
|
||||||
|
float weight;
|
||||||
|
};
|
||||||
|
|
||||||
enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
|
enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
|
||||||
|
|
||||||
/** This class allows users to create a web server with their ESP nodes.
|
/** This class allows users to create a web server with their ESP nodes.
|
||||||
@ -320,12 +326,15 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|||||||
/// This web handle is not trivial.
|
/// This web handle is not trivial.
|
||||||
bool isRequestHandlerTrivial() override;
|
bool isRequestHandlerTrivial() override;
|
||||||
|
|
||||||
|
void add_entity_to_sorting_list(EntityBase *entity, float weight);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void schedule_(std::function<void()> &&f);
|
void schedule_(std::function<void()> &&f);
|
||||||
friend ListEntitiesIterator;
|
friend ListEntitiesIterator;
|
||||||
web_server_base::WebServerBase *base_;
|
web_server_base::WebServerBase *base_;
|
||||||
AsyncEventSource events_{"/events"};
|
AsyncEventSource events_{"/events"};
|
||||||
ListEntitiesIterator entities_iterator_;
|
ListEntitiesIterator entities_iterator_;
|
||||||
|
std::map<EntityBase *, SortingComponents> sorting_entitys_;
|
||||||
#if USE_WEBSERVER_VERSION == 1
|
#if USE_WEBSERVER_VERSION == 1
|
||||||
const char *css_url_{nullptr};
|
const char *css_url_{nullptr};
|
||||||
const char *js_url_{nullptr};
|
const char *js_url_{nullptr};
|
||||||
|
@ -37,4 +37,4 @@ async def to_code(config):
|
|||||||
cg.add_library("FS", None)
|
cg.add_library("FS", None)
|
||||||
cg.add_library("Update", None)
|
cg.add_library("Update", None)
|
||||||
# https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json
|
# https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json
|
||||||
cg.add_library("esphome/ESPAsyncWebServer-esphome", "3.2.0")
|
cg.add_library("esphome/ESPAsyncWebServer-esphome", "3.2.2")
|
||||||
|
@ -375,8 +375,8 @@ void WeikaiChannel::set_baudrate_() {
|
|||||||
this->parent_->page1_ = false; // switch back to page 0
|
this->parent_->page1_ = false; // switch back to page 0
|
||||||
this->reg(WKREG_SPAGE) = 0;
|
this->reg(WKREG_SPAGE) = 0;
|
||||||
|
|
||||||
ESP_LOGV(TAG, " Crystal=%d baudrate=%d => registers [%d %d %d]", this->parent_->crystal_, this->baud_rate_,
|
ESP_LOGV(TAG, " Crystal=%" PRId32 " baudrate=%" PRId32 " => registers [%d %d %d]", this->parent_->crystal_,
|
||||||
baud_high, baud_low, baud_dec);
|
this->baud_rate_, baud_high, baud_low, baud_dec);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool WeikaiChannel::tx_fifo_is_not_empty_() { return this->reg(WKREG_FSR) & FSR_TFDAT; }
|
inline bool WeikaiChannel::tx_fifo_is_not_empty_() { return this->reg(WKREG_FSR) & FSR_TFDAT; }
|
||||||
|
@ -58,7 +58,7 @@ void WiFiComponent::setup() {
|
|||||||
|
|
||||||
void WiFiComponent::start() {
|
void WiFiComponent::start() {
|
||||||
ESP_LOGCONFIG(TAG, "Starting WiFi...");
|
ESP_LOGCONFIG(TAG, "Starting WiFi...");
|
||||||
ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str());
|
ESP_LOGCONFIG(TAG, " Local MAC: %s", get_mac_address_pretty().c_str());
|
||||||
this->last_connected_ = millis();
|
this->last_connected_ = millis();
|
||||||
|
|
||||||
uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL;
|
uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL;
|
||||||
@ -135,7 +135,7 @@ void WiFiComponent::loop() {
|
|||||||
|
|
||||||
switch (this->state_) {
|
switch (this->state_) {
|
||||||
case WIFI_COMPONENT_STATE_COOLDOWN: {
|
case WIFI_COMPONENT_STATE_COOLDOWN: {
|
||||||
this->status_set_warning();
|
this->status_set_warning("waiting to reconnect");
|
||||||
if (millis() - this->action_started_ > 5000) {
|
if (millis() - this->action_started_ > 5000) {
|
||||||
if (this->fast_connect_ || this->retry_hidden_) {
|
if (this->fast_connect_ || this->retry_hidden_) {
|
||||||
this->start_connecting(this->sta_[0], false);
|
this->start_connecting(this->sta_[0], false);
|
||||||
@ -146,13 +146,13 @@ void WiFiComponent::loop() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WIFI_COMPONENT_STATE_STA_SCANNING: {
|
case WIFI_COMPONENT_STATE_STA_SCANNING: {
|
||||||
this->status_set_warning();
|
this->status_set_warning("scanning for networks");
|
||||||
this->check_scanning_finished();
|
this->check_scanning_finished();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WIFI_COMPONENT_STATE_STA_CONNECTING:
|
case WIFI_COMPONENT_STATE_STA_CONNECTING:
|
||||||
case WIFI_COMPONENT_STATE_STA_CONNECTING_2: {
|
case WIFI_COMPONENT_STATE_STA_CONNECTING_2: {
|
||||||
this->status_set_warning();
|
this->status_set_warning("associating to network");
|
||||||
this->check_connecting_finished();
|
this->check_connecting_finished();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ async def to_code(config):
|
|||||||
# the '+1' modifier is relative to the device's own address that will
|
# the '+1' modifier is relative to the device's own address that will
|
||||||
# be automatically added to the provided list.
|
# be automatically added to the provided list.
|
||||||
cg.add_build_flag(f"-DCONFIG_WIREGUARD_MAX_SRC_IPS={len(allowed_ips) + 1}")
|
cg.add_build_flag(f"-DCONFIG_WIREGUARD_MAX_SRC_IPS={len(allowed_ips) + 1}")
|
||||||
cg.add_library("droscy/esp_wireguard", "0.4.0")
|
cg.add_library("droscy/esp_wireguard", "0.4.1")
|
||||||
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "wl_134.h"
|
#include "wl_134.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace wl_134 {
|
namespace wl_134 {
|
||||||
|
|
||||||
@ -71,7 +73,7 @@ Wl134Component::Rfid134Error Wl134Component::read_packet_() {
|
|||||||
ESP_LOGV(TAG, "isData: %s", reading.isData ? "true" : "false");
|
ESP_LOGV(TAG, "isData: %s", reading.isData ? "true" : "false");
|
||||||
ESP_LOGV(TAG, "isAnimal: %s", reading.isAnimal ? "true" : "false");
|
ESP_LOGV(TAG, "isAnimal: %s", reading.isAnimal ? "true" : "false");
|
||||||
ESP_LOGV(TAG, "Reserved0: %d", reading.reserved0);
|
ESP_LOGV(TAG, "Reserved0: %d", reading.reserved0);
|
||||||
ESP_LOGV(TAG, "Reserved1: %d", reading.reserved1);
|
ESP_LOGV(TAG, "Reserved1: %" PRId32, reading.reserved1);
|
||||||
|
|
||||||
char buf[20];
|
char buf[20];
|
||||||
sprintf(buf, "%03d%012lld", reading.country, reading.id);
|
sprintf(buf, "%03d%012lld", reading.country, reading.id);
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace xgzp68xx {
|
namespace xgzp68xx {
|
||||||
|
|
||||||
@ -37,8 +39,8 @@ void XGZP68XXComponent::update() {
|
|||||||
temperature_raw = encode_uint16(data[3], data[4]);
|
temperature_raw = encode_uint16(data[3], data[4]);
|
||||||
|
|
||||||
// Convert the pressure data to hPa
|
// Convert the pressure data to hPa
|
||||||
ESP_LOGV(TAG, "Got raw pressure=%d, raw temperature=%d ", pressure_raw, temperature_raw);
|
ESP_LOGV(TAG, "Got raw pressure=%" PRIu32 ", raw temperature=%u", pressure_raw, temperature_raw);
|
||||||
ESP_LOGV(TAG, "K value is %d ", this->k_value_);
|
ESP_LOGV(TAG, "K value is %u", this->k_value_);
|
||||||
|
|
||||||
// The most significant bit of both pressure and temperature will be 1 to indicate a negative value.
|
// The most significant bit of both pressure and temperature will be 1 to indicate a negative value.
|
||||||
// This is directly from the datasheet, and the calculations below will handle this.
|
// This is directly from the datasheet, and the calculations below will handle this.
|
||||||
|
@ -49,6 +49,7 @@ CONF_AFTER = "after"
|
|||||||
CONF_ALLOW_OTHER_USES = "allow_other_uses"
|
CONF_ALLOW_OTHER_USES = "allow_other_uses"
|
||||||
CONF_ALPHA = "alpha"
|
CONF_ALPHA = "alpha"
|
||||||
CONF_ALTITUDE = "altitude"
|
CONF_ALTITUDE = "altitude"
|
||||||
|
CONF_AMBIENT_LIGHT = "ambient_light"
|
||||||
CONF_ANALOG = "analog"
|
CONF_ANALOG = "analog"
|
||||||
CONF_AND = "and"
|
CONF_AND = "and"
|
||||||
CONF_ANGLE = "angle"
|
CONF_ANGLE = "angle"
|
||||||
@ -904,6 +905,8 @@ CONF_WARM_WHITE = "warm_white"
|
|||||||
CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature"
|
CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature"
|
||||||
CONF_WATCHDOG_THRESHOLD = "watchdog_threshold"
|
CONF_WATCHDOG_THRESHOLD = "watchdog_threshold"
|
||||||
CONF_WEB_SERVER = "web_server"
|
CONF_WEB_SERVER = "web_server"
|
||||||
|
CONF_WEB_SERVER_ID = "web_server_id"
|
||||||
|
CONF_WEB_SERVER_SORTING_WEIGHT = "web_server_sorting_weight"
|
||||||
CONF_WEIGHT = "weight"
|
CONF_WEIGHT = "weight"
|
||||||
CONF_WHILE = "while"
|
CONF_WHILE = "while"
|
||||||
CONF_WHITE = "white"
|
CONF_WHITE = "white"
|
||||||
@ -1032,6 +1035,7 @@ UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm"
|
|||||||
UNIT_MICROSILVERTS_PER_HOUR = "µSv/h"
|
UNIT_MICROSILVERTS_PER_HOUR = "µSv/h"
|
||||||
UNIT_MICROTESLA = "µT"
|
UNIT_MICROTESLA = "µT"
|
||||||
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
|
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
|
||||||
|
UNIT_MILLIMETER = "mm"
|
||||||
UNIT_MILLISECOND = "ms"
|
UNIT_MILLISECOND = "ms"
|
||||||
UNIT_MILLISIEMENS_PER_CENTIMETER = "mS/cm"
|
UNIT_MILLISIEMENS_PER_CENTIMETER = "mS/cm"
|
||||||
UNIT_MINUTE = "min"
|
UNIT_MINUTE = "min"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <utility>
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <utility>
|
|
||||||
#include <cinttypes>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
@ -140,8 +140,8 @@ void Component::set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std:
|
|||||||
float backoff_increase_factor) { // NOLINT
|
float backoff_increase_factor) { // NOLINT
|
||||||
App.scheduler.set_retry(this, "", initial_wait_time, max_attempts, std::move(f), backoff_increase_factor);
|
App.scheduler.set_retry(this, "", initial_wait_time, max_attempts, std::move(f), backoff_increase_factor);
|
||||||
}
|
}
|
||||||
bool Component::is_failed() { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; }
|
bool Component::is_failed() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; }
|
||||||
bool Component::is_ready() {
|
bool Component::is_ready() const {
|
||||||
return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP ||
|
return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP ||
|
||||||
(this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP;
|
(this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP;
|
||||||
}
|
}
|
||||||
|
@ -118,9 +118,9 @@ class Component {
|
|||||||
*/
|
*/
|
||||||
virtual void mark_failed();
|
virtual void mark_failed();
|
||||||
|
|
||||||
bool is_failed();
|
bool is_failed() const;
|
||||||
|
|
||||||
bool is_ready();
|
bool is_ready() const;
|
||||||
|
|
||||||
virtual bool can_proceed();
|
virtual bool can_proceed();
|
||||||
|
|
||||||
|
@ -433,6 +433,10 @@ int8_t step_to_accuracy_decimals(float step) {
|
|||||||
return str.length() - dot_pos - 1;
|
return str.length() - dot_pos - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const std::string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"0123456789+/";
|
||||||
|
|
||||||
static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); }
|
static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); }
|
||||||
|
|
||||||
std::string base64_encode(const std::vector<uint8_t> &buf) { return base64_encode(buf.data(), buf.size()); }
|
std::string base64_encode(const std::vector<uint8_t> &buf) { return base64_encode(buf.data(), buf.size()); }
|
||||||
|
@ -435,10 +435,6 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
|
|||||||
/// Derive accuracy in decimals from an increment step.
|
/// Derive accuracy in decimals from an increment step.
|
||||||
int8_t step_to_accuracy_decimals(float step);
|
int8_t step_to_accuracy_decimals(float step);
|
||||||
|
|
||||||
static const std::string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
|
||||||
"0123456789+/";
|
|
||||||
|
|
||||||
std::string base64_encode(const uint8_t *buf, size_t buf_len);
|
std::string base64_encode(const uint8_t *buf, size_t buf_len);
|
||||||
std::string base64_encode(const std::vector<uint8_t> &buf);
|
std::string base64_encode(const std::vector<uint8_t> &buf);
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ lib_deps =
|
|||||||
SPI ; spi (Arduino built-in)
|
SPI ; spi (Arduino built-in)
|
||||||
Wire ; i2c (Arduino built-int)
|
Wire ; i2c (Arduino built-int)
|
||||||
heman/AsyncMqttClient-esphome@1.0.0 ; mqtt
|
heman/AsyncMqttClient-esphome@1.0.0 ; mqtt
|
||||||
esphome/ESPAsyncWebServer-esphome@3.2.0 ; web_server_base
|
esphome/ESPAsyncWebServer-esphome@3.2.2 ; web_server_base
|
||||||
fastled/FastLED@3.3.2 ; fastled_base
|
fastled/FastLED@3.3.2 ; fastled_base
|
||||||
mikalhart/TinyGPSPlus@1.0.2 ; gps
|
mikalhart/TinyGPSPlus@1.0.2 ; gps
|
||||||
freekode/TM1651@1.0.1 ; tm1651
|
freekode/TM1651@1.0.1 ; tm1651
|
||||||
@ -94,7 +94,7 @@ lib_deps =
|
|||||||
ESP8266mDNS ; mdns (Arduino built-in)
|
ESP8266mDNS ; mdns (Arduino built-in)
|
||||||
DNSServer ; captive_portal (Arduino built-in)
|
DNSServer ; captive_portal (Arduino built-in)
|
||||||
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
||||||
droscy/esp_wireguard@0.4.0 ; wireguard
|
droscy/esp_wireguard@0.4.1 ; wireguard
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:arduino.build_flags}
|
${common:arduino.build_flags}
|
||||||
-Wno-nonnull-compare
|
-Wno-nonnull-compare
|
||||||
@ -124,7 +124,7 @@ lib_deps =
|
|||||||
DNSServer ; captive_portal (Arduino built-in)
|
DNSServer ; captive_portal (Arduino built-in)
|
||||||
esphome/ESP32-audioI2S@2.0.7 ; i2s_audio
|
esphome/ESP32-audioI2S@2.0.7 ; i2s_audio
|
||||||
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
||||||
droscy/esp_wireguard@0.4.0 ; wireguard
|
droscy/esp_wireguard@0.4.1 ; wireguard
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:arduino.build_flags}
|
${common:arduino.build_flags}
|
||||||
-DUSE_ESP32
|
-DUSE_ESP32
|
||||||
@ -143,7 +143,7 @@ framework = espidf
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${common:idf.lib_deps}
|
${common:idf.lib_deps}
|
||||||
espressif/esp32-camera@1.0.0 ; esp32_camera
|
espressif/esp32-camera@1.0.0 ; esp32_camera
|
||||||
droscy/esp_wireguard@0.4.0 ; wireguard
|
droscy/esp_wireguard@0.4.1 ; wireguard
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:idf.build_flags}
|
${common:idf.build_flags}
|
||||||
-Wno-nonnull-compare
|
-Wno-nonnull-compare
|
||||||
@ -174,6 +174,8 @@ build_flags =
|
|||||||
extends = common:arduino
|
extends = common:arduino
|
||||||
platform = libretiny
|
platform = libretiny
|
||||||
framework = arduino
|
framework = arduino
|
||||||
|
lib_deps =
|
||||||
|
droscy/esp_wireguard@0.4.1 ; wireguard
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:arduino.build_flags}
|
${common:arduino.build_flags}
|
||||||
-DUSE_LIBRETINY
|
-DUSE_LIBRETINY
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
pylint==3.1.0
|
pylint==3.1.0
|
||||||
flake8==7.0.0 # also change in .pre-commit-config.yaml when updating
|
flake8==7.0.0 # also change in .pre-commit-config.yaml when updating
|
||||||
black==24.4.0 # also change in .pre-commit-config.yaml when updating
|
black==24.4.2 # also change in .pre-commit-config.yaml when updating
|
||||||
pyupgrade==3.15.2 # also change in .pre-commit-config.yaml when updating
|
pyupgrade==3.15.2 # also change in .pre-commit-config.yaml when updating
|
||||||
pre-commit
|
pre-commit
|
||||||
|
|
||||||
# Unit tests
|
# Unit tests
|
||||||
pytest==8.2.0
|
pytest==8.2.0
|
||||||
pytest-cov==4.1.0
|
pytest-cov==5.0.0
|
||||||
pytest-mock==3.14.0
|
pytest-mock==3.14.0
|
||||||
pytest-asyncio==0.23.6
|
pytest-asyncio==0.23.6
|
||||||
asyncmock==0.4.2
|
asyncmock==0.4.2
|
||||||
|
11
tests/components/ltr_als_ps/common.yaml
Normal file
11
tests/components/ltr_als_ps/common.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
sensor:
|
||||||
|
- platform: ltr_als_ps
|
||||||
|
address: 0x23
|
||||||
|
i2c_id: i2c_als_ps
|
||||||
|
gain: 1x
|
||||||
|
integration_time: 100ms
|
||||||
|
ps_cooldown: 5 s
|
||||||
|
ambient_light: "Ambient light"
|
||||||
|
full_spectrum_counts: "Full spectrum counts"
|
||||||
|
infrared_counts: "Infrared counts"
|
||||||
|
actual_gain: "Actual gain"
|
6
tests/components/ltr_als_ps/test.esp32-c3-idf.yaml
Normal file
6
tests/components/ltr_als_ps/test.esp32-c3-idf.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
i2c:
|
||||||
|
- id: i2c_als_ps
|
||||||
|
scl: 5
|
||||||
|
sda: 4
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user