mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 23:48:11 +01:00
Merge branch 'dev' into optolink
This commit is contained in:
commit
28c0094600
61 changed files with 2421 additions and 190 deletions
|
@ -40,6 +40,7 @@
|
||||||
"yaml.customTags": [
|
"yaml.customTags": [
|
||||||
"!secret scalar",
|
"!secret scalar",
|
||||||
"!lambda scalar",
|
"!lambda scalar",
|
||||||
|
"!extend scalar",
|
||||||
"!include_dir_named scalar",
|
"!include_dir_named scalar",
|
||||||
"!include_dir_list scalar",
|
"!include_dir_list scalar",
|
||||||
"!include_dir_merge_list scalar",
|
"!include_dir_merge_list scalar",
|
||||||
|
|
18
.vscode/tasks.json
vendored
18
.vscode/tasks.json
vendored
|
@ -36,6 +36,24 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Generate proto files",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${command:python.interpreterPath}",
|
||||||
|
"args": [
|
||||||
|
"./script/api_protobuf/api_protobuf.py"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "never",
|
||||||
|
"close": true,
|
||||||
|
"panel": "new"
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ esphome/components/addressable_light/* @justfalter
|
||||||
esphome/components/airthings_ble/* @jeromelaban
|
esphome/components/airthings_ble/* @jeromelaban
|
||||||
esphome/components/airthings_wave_mini/* @ncareau
|
esphome/components/airthings_wave_mini/* @ncareau
|
||||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||||
|
esphome/components/alarm_control_panel/* @grahambrown11
|
||||||
esphome/components/am43/* @buxtronix
|
esphome/components/am43/* @buxtronix
|
||||||
esphome/components/am43/cover/* @buxtronix
|
esphome/components/am43/cover/* @buxtronix
|
||||||
esphome/components/am43/sensor/* @buxtronix
|
esphome/components/am43/sensor/* @buxtronix
|
||||||
|
@ -278,6 +279,7 @@ esphome/components/tca9548a/* @andreashergert1984
|
||||||
esphome/components/tcl112/* @glmnet
|
esphome/components/tcl112/* @glmnet
|
||||||
esphome/components/tee501/* @Stock-M
|
esphome/components/tee501/* @Stock-M
|
||||||
esphome/components/teleinfo/* @0hax
|
esphome/components/teleinfo/* @0hax
|
||||||
|
esphome/components/template/alarm_control_panel/* @grahambrown11
|
||||||
esphome/components/thermostat/* @kbx81
|
esphome/components/thermostat/* @kbx81
|
||||||
esphome/components/time/* @OttoWinter
|
esphome/components/time/* @OttoWinter
|
||||||
esphome/components/tlc5947/* @rnauber
|
esphome/components/tlc5947/* @rnauber
|
||||||
|
|
165
esphome/components/alarm_control_panel/__init__.py
Normal file
165
esphome/components/alarm_control_panel/__init__.py
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation
|
||||||
|
from esphome.automation import maybe_simple_id
|
||||||
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_ON_STATE,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
|
CONF_CODE,
|
||||||
|
)
|
||||||
|
from esphome.cpp_helpers import setup_entity
|
||||||
|
|
||||||
|
CODEOWNERS = ["@grahambrown11"]
|
||||||
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
|
CONF_ON_TRIGGERED = "on_triggered"
|
||||||
|
CONF_ON_CLEARED = "on_cleared"
|
||||||
|
|
||||||
|
alarm_control_panel_ns = cg.esphome_ns.namespace("alarm_control_panel")
|
||||||
|
AlarmControlPanel = alarm_control_panel_ns.class_("AlarmControlPanel", cg.EntityBase)
|
||||||
|
|
||||||
|
StateTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"StateTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
TriggeredTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"TriggeredTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ClearedTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"ClearedTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ArmAwayAction = alarm_control_panel_ns.class_("ArmAwayAction", automation.Action)
|
||||||
|
ArmHomeAction = alarm_control_panel_ns.class_("ArmHomeAction", automation.Action)
|
||||||
|
DisarmAction = alarm_control_panel_ns.class_("DisarmAction", automation.Action)
|
||||||
|
PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action)
|
||||||
|
TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action)
|
||||||
|
AlarmControlPanelCondition = alarm_control_panel_ns.class_(
|
||||||
|
"AlarmControlPanelCondition", automation.Condition
|
||||||
|
)
|
||||||
|
|
||||||
|
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AlarmControlPanel),
|
||||||
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(AlarmControlPanel),
|
||||||
|
cv.Optional(CONF_CODE): cv.templatable(cv.string),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(AlarmControlPanel),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_alarm_control_panel_core_(var, config):
|
||||||
|
await setup_entity(var, config)
|
||||||
|
for conf in config.get(CONF_ON_STATE, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_TRIGGERED, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_CLEARED, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
|
||||||
|
|
||||||
|
async def register_alarm_control_panel(var, config):
|
||||||
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
var = cg.Pvariable(config[CONF_ID], var)
|
||||||
|
cg.add(cg.App.register_alarm_control_panel(var))
|
||||||
|
await setup_alarm_control_panel_core_(var, config)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"alarm_control_panel.arm_away", ArmAwayAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def alarm_action_arm_away_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
if CONF_CODE in config:
|
||||||
|
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
|
||||||
|
cg.add(var.set_code(templatable_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"alarm_control_panel.arm_home", ArmHomeAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def alarm_action_arm_home_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
if CONF_CODE in config:
|
||||||
|
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
|
||||||
|
cg.add(var.set_code(templatable_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"alarm_control_panel.disarm", DisarmAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def alarm_action_disarm_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
if CONF_CODE in config:
|
||||||
|
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
|
||||||
|
cg.add(var.set_code(templatable_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"alarm_control_panel.pending", PendingAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def alarm_action_pending_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"alarm_control_panel.triggered", TriggeredAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def alarm_action_trigger_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition(
|
||||||
|
"alarm_control_panel.is_armed",
|
||||||
|
AlarmControlPanelCondition,
|
||||||
|
ALARM_CONTROL_PANEL_CONDITION_SCHEMA,
|
||||||
|
)
|
||||||
|
async def alarm_control_panel_is_armed_to_code(
|
||||||
|
config, condition_id, template_arg, args
|
||||||
|
):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
return cg.new_Pvariable(condition_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
|
@coroutine_with_priority(100.0)
|
||||||
|
async def to_code(config):
|
||||||
|
cg.add_global(alarm_control_panel_ns.using)
|
||||||
|
cg.add_define("USE_ALARM_CONTROL_PANEL")
|
111
esphome/components/alarm_control_panel/alarm_control_panel.cpp
Normal file
111
esphome/components/alarm_control_panel/alarm_control_panel.cpp
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "alarm_control_panel.h"
|
||||||
|
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alarm_control_panel {
|
||||||
|
|
||||||
|
static const char *const TAG = "alarm_control_panel";
|
||||||
|
|
||||||
|
AlarmControlPanelCall AlarmControlPanel::make_call() { return AlarmControlPanelCall(this); }
|
||||||
|
|
||||||
|
bool AlarmControlPanel::is_state_armed(AlarmControlPanelState state) {
|
||||||
|
switch (state) {
|
||||||
|
case ACP_STATE_ARMED_AWAY:
|
||||||
|
case ACP_STATE_ARMED_HOME:
|
||||||
|
case ACP_STATE_ARMED_NIGHT:
|
||||||
|
case ACP_STATE_ARMED_VACATION:
|
||||||
|
case ACP_STATE_ARMED_CUSTOM_BYPASS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
||||||
|
this->last_update_ = millis();
|
||||||
|
if (state != this->current_state_) {
|
||||||
|
auto prev_state = this->current_state_;
|
||||||
|
ESP_LOGD(TAG, "Set state to: %s, previous: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
|
||||||
|
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
||||||
|
this->current_state_ = state;
|
||||||
|
this->state_callback_.call();
|
||||||
|
if (state == ACP_STATE_TRIGGERED) {
|
||||||
|
this->triggered_callback_.call();
|
||||||
|
}
|
||||||
|
if (prev_state == ACP_STATE_TRIGGERED) {
|
||||||
|
this->cleared_callback_.call();
|
||||||
|
}
|
||||||
|
if (state == this->desired_state_) {
|
||||||
|
// only store when in the desired state
|
||||||
|
this->pref_.save(&state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_state_callback(std::function<void()> &&callback) {
|
||||||
|
this->state_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_triggered_callback(std::function<void()> &&callback) {
|
||||||
|
this->triggered_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_cleared_callback(std::function<void()> &&callback) {
|
||||||
|
this->cleared_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::arm_away(optional<std::string> code) {
|
||||||
|
auto call = this->make_call();
|
||||||
|
call.arm_away();
|
||||||
|
if (code.has_value())
|
||||||
|
call.set_code(code.value());
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::arm_home(optional<std::string> code) {
|
||||||
|
auto call = this->make_call();
|
||||||
|
call.arm_home();
|
||||||
|
if (code.has_value())
|
||||||
|
call.set_code(code.value());
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::arm_night(optional<std::string> code) {
|
||||||
|
auto call = this->make_call();
|
||||||
|
call.arm_night();
|
||||||
|
if (code.has_value())
|
||||||
|
call.set_code(code.value());
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::arm_vacation(optional<std::string> code) {
|
||||||
|
auto call = this->make_call();
|
||||||
|
call.arm_vacation();
|
||||||
|
if (code.has_value())
|
||||||
|
call.set_code(code.value());
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::arm_custom_bypass(optional<std::string> code) {
|
||||||
|
auto call = this->make_call();
|
||||||
|
call.arm_custom_bypass();
|
||||||
|
if (code.has_value())
|
||||||
|
call.set_code(code.value());
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::disarm(optional<std::string> code) {
|
||||||
|
auto call = this->make_call();
|
||||||
|
call.disarm();
|
||||||
|
if (code.has_value())
|
||||||
|
call.set_code(code.value());
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace alarm_control_panel
|
||||||
|
} // namespace esphome
|
136
esphome/components/alarm_control_panel/alarm_control_panel.h
Normal file
136
esphome/components/alarm_control_panel/alarm_control_panel.h
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "alarm_control_panel_call.h"
|
||||||
|
#include "alarm_control_panel_state.h"
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/entity_base.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alarm_control_panel {
|
||||||
|
|
||||||
|
enum AlarmControlPanelFeature : uint8_t {
|
||||||
|
// Matches Home Assistant values
|
||||||
|
ACP_FEAT_ARM_HOME = 1 << 0,
|
||||||
|
ACP_FEAT_ARM_AWAY = 1 << 1,
|
||||||
|
ACP_FEAT_ARM_NIGHT = 1 << 2,
|
||||||
|
ACP_FEAT_TRIGGER = 1 << 3,
|
||||||
|
ACP_FEAT_ARM_CUSTOM_BYPASS = 1 << 4,
|
||||||
|
ACP_FEAT_ARM_VACATION = 1 << 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarmControlPanel : public EntityBase {
|
||||||
|
public:
|
||||||
|
/** Make a AlarmControlPanelCall
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
AlarmControlPanelCall make_call();
|
||||||
|
|
||||||
|
/** Set the state of the alarm_control_panel.
|
||||||
|
*
|
||||||
|
* @param state The AlarmControlPanelState.
|
||||||
|
*/
|
||||||
|
void publish_state(AlarmControlPanelState state);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel changes
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_state_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel chanes to triggered
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_triggered_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel clears from triggered
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_cleared_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** A numeric representation of the supported features as per HomeAssistant
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual uint32_t get_supported_features() const = 0;
|
||||||
|
|
||||||
|
/** Returns if the alarm_control_panel has a code
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual bool get_requires_code() const = 0;
|
||||||
|
|
||||||
|
/** Returns if the alarm_control_panel requires a code to arm
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
virtual bool get_requires_code_to_arm() const = 0;
|
||||||
|
|
||||||
|
/** arm the alarm in away mode
|
||||||
|
*
|
||||||
|
* @param code The code
|
||||||
|
*/
|
||||||
|
void arm_away(optional<std::string> code = nullopt);
|
||||||
|
|
||||||
|
/** arm the alarm in home mode
|
||||||
|
*
|
||||||
|
* @param code The code
|
||||||
|
*/
|
||||||
|
void arm_home(optional<std::string> code = nullopt);
|
||||||
|
|
||||||
|
/** arm the alarm in night mode
|
||||||
|
*
|
||||||
|
* @param code The code
|
||||||
|
*/
|
||||||
|
void arm_night(optional<std::string> code = nullopt);
|
||||||
|
|
||||||
|
/** arm the alarm in vacation mode
|
||||||
|
*
|
||||||
|
* @param code The code
|
||||||
|
*/
|
||||||
|
void arm_vacation(optional<std::string> code = nullopt);
|
||||||
|
|
||||||
|
/** arm the alarm in custom bypass mode
|
||||||
|
*
|
||||||
|
* @param code The code
|
||||||
|
*/
|
||||||
|
void arm_custom_bypass(optional<std::string> code = nullopt);
|
||||||
|
|
||||||
|
/** disarm the alarm
|
||||||
|
*
|
||||||
|
* @param code The code
|
||||||
|
*/
|
||||||
|
void disarm(optional<std::string> code = nullopt);
|
||||||
|
|
||||||
|
/** Get the state
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
AlarmControlPanelState get_state() const { return this->current_state_; }
|
||||||
|
|
||||||
|
// is the state one of the armed states
|
||||||
|
bool is_state_armed(AlarmControlPanelState state);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend AlarmControlPanelCall;
|
||||||
|
// in order to store last panel state in flash
|
||||||
|
ESPPreferenceObject pref_;
|
||||||
|
// current state
|
||||||
|
AlarmControlPanelState current_state_;
|
||||||
|
// the desired (or previous) state
|
||||||
|
AlarmControlPanelState desired_state_;
|
||||||
|
// last time the state was updated
|
||||||
|
uint32_t last_update_;
|
||||||
|
// the call control function
|
||||||
|
virtual void control(const AlarmControlPanelCall &call) = 0;
|
||||||
|
// state callback
|
||||||
|
CallbackManager<void()> state_callback_{};
|
||||||
|
// trigger callback
|
||||||
|
CallbackManager<void()> triggered_callback_{};
|
||||||
|
// clear callback
|
||||||
|
CallbackManager<void()> cleared_callback_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace alarm_control_panel
|
||||||
|
} // namespace esphome
|
|
@ -0,0 +1,99 @@
|
||||||
|
#include "alarm_control_panel_call.h"
|
||||||
|
|
||||||
|
#include "alarm_control_panel.h"
|
||||||
|
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alarm_control_panel {
|
||||||
|
|
||||||
|
static const char *const TAG = "alarm_control_panel";
|
||||||
|
|
||||||
|
AlarmControlPanelCall::AlarmControlPanelCall(AlarmControlPanel *parent) : parent_(parent) {}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::set_code(const std::string &code) {
|
||||||
|
this->code_ = code;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::arm_away() {
|
||||||
|
this->state_ = ACP_STATE_ARMED_AWAY;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::arm_home() {
|
||||||
|
this->state_ = ACP_STATE_ARMED_HOME;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::arm_night() {
|
||||||
|
this->state_ = ACP_STATE_ARMED_NIGHT;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::arm_vacation() {
|
||||||
|
this->state_ = ACP_STATE_ARMED_VACATION;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::arm_custom_bypass() {
|
||||||
|
this->state_ = ACP_STATE_ARMED_CUSTOM_BYPASS;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::disarm() {
|
||||||
|
this->state_ = ACP_STATE_DISARMED;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::pending() {
|
||||||
|
this->state_ = ACP_STATE_PENDING;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlarmControlPanelCall &AlarmControlPanelCall::triggered() {
|
||||||
|
this->state_ = ACP_STATE_TRIGGERED;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const optional<AlarmControlPanelState> &AlarmControlPanelCall::get_state() const { return this->state_; }
|
||||||
|
const optional<std::string> &AlarmControlPanelCall::get_code() const { return this->code_; }
|
||||||
|
|
||||||
|
void AlarmControlPanelCall::validate_() {
|
||||||
|
if (this->state_.has_value()) {
|
||||||
|
auto state = *this->state_;
|
||||||
|
if (this->parent_->is_state_armed(state) && this->parent_->get_state() != ACP_STATE_DISARMED) {
|
||||||
|
ESP_LOGW(TAG, "Cannot arm when not disarmed");
|
||||||
|
this->state_.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == ACP_STATE_PENDING && this->parent_->get_state() == ACP_STATE_DISARMED) {
|
||||||
|
ESP_LOGW(TAG, "Cannot trip alarm when disarmed");
|
||||||
|
this->state_.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == ACP_STATE_DISARMED &&
|
||||||
|
!(this->parent_->is_state_armed(this->parent_->get_state()) ||
|
||||||
|
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_ARMING ||
|
||||||
|
this->parent_->get_state() == ACP_STATE_TRIGGERED)) {
|
||||||
|
ESP_LOGW(TAG, "Cannot disarm when not armed");
|
||||||
|
this->state_.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == ACP_STATE_ARMED_HOME && (this->parent_->get_supported_features() & ACP_FEAT_ARM_HOME) == 0) {
|
||||||
|
ESP_LOGW(TAG, "Cannot arm home when not supported");
|
||||||
|
this->state_.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanelCall::perform() {
|
||||||
|
this->validate_();
|
||||||
|
if (this->state_) {
|
||||||
|
this->parent_->control(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace alarm_control_panel
|
||||||
|
} // namespace esphome
|
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "alarm_control_panel_state.h"
|
||||||
|
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alarm_control_panel {
|
||||||
|
|
||||||
|
class AlarmControlPanel;
|
||||||
|
|
||||||
|
class AlarmControlPanelCall {
|
||||||
|
public:
|
||||||
|
AlarmControlPanelCall(AlarmControlPanel *parent);
|
||||||
|
|
||||||
|
AlarmControlPanelCall &set_code(const std::string &code);
|
||||||
|
AlarmControlPanelCall &arm_away();
|
||||||
|
AlarmControlPanelCall &arm_home();
|
||||||
|
AlarmControlPanelCall &arm_night();
|
||||||
|
AlarmControlPanelCall &arm_vacation();
|
||||||
|
AlarmControlPanelCall &arm_custom_bypass();
|
||||||
|
AlarmControlPanelCall &disarm();
|
||||||
|
AlarmControlPanelCall &pending();
|
||||||
|
AlarmControlPanelCall &triggered();
|
||||||
|
|
||||||
|
void perform();
|
||||||
|
const optional<AlarmControlPanelState> &get_state() const;
|
||||||
|
const optional<std::string> &get_code() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *parent_;
|
||||||
|
optional<std::string> code_{};
|
||||||
|
optional<AlarmControlPanelState> state_{};
|
||||||
|
void validate_();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace alarm_control_panel
|
||||||
|
} // namespace esphome
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include "alarm_control_panel_state.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alarm_control_panel {
|
||||||
|
|
||||||
|
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state) {
|
||||||
|
switch (state) {
|
||||||
|
case ACP_STATE_DISARMED:
|
||||||
|
return LOG_STR("DISARMED");
|
||||||
|
case ACP_STATE_ARMED_HOME:
|
||||||
|
return LOG_STR("ARMED_HOME");
|
||||||
|
case ACP_STATE_ARMED_AWAY:
|
||||||
|
return LOG_STR("ARMED_AWAY");
|
||||||
|
case ACP_STATE_ARMED_NIGHT:
|
||||||
|
return LOG_STR("NIGHT");
|
||||||
|
case ACP_STATE_ARMED_VACATION:
|
||||||
|
return LOG_STR("ARMED_VACATION");
|
||||||
|
case ACP_STATE_ARMED_CUSTOM_BYPASS:
|
||||||
|
return LOG_STR("ARMED_CUSTOM_BYPASS");
|
||||||
|
case ACP_STATE_PENDING:
|
||||||
|
return LOG_STR("PENDING");
|
||||||
|
case ACP_STATE_ARMING:
|
||||||
|
return LOG_STR("ARMING");
|
||||||
|
case ACP_STATE_DISARMING:
|
||||||
|
return LOG_STR("DISARMING");
|
||||||
|
case ACP_STATE_TRIGGERED:
|
||||||
|
return LOG_STR("TRIGGERED");
|
||||||
|
default:
|
||||||
|
return LOG_STR("UNKNOWN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace alarm_control_panel
|
||||||
|
} // namespace esphome
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alarm_control_panel {
|
||||||
|
|
||||||
|
enum AlarmControlPanelState : uint8_t {
|
||||||
|
ACP_STATE_DISARMED = 0,
|
||||||
|
ACP_STATE_ARMED_HOME = 1,
|
||||||
|
ACP_STATE_ARMED_AWAY = 2,
|
||||||
|
ACP_STATE_ARMED_NIGHT = 3,
|
||||||
|
ACP_STATE_ARMED_VACATION = 4,
|
||||||
|
ACP_STATE_ARMED_CUSTOM_BYPASS = 5,
|
||||||
|
ACP_STATE_PENDING = 6,
|
||||||
|
ACP_STATE_ARMING = 7,
|
||||||
|
ACP_STATE_DISARMING = 8,
|
||||||
|
ACP_STATE_TRIGGERED = 9
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Returns a string representation of the state.
|
||||||
|
*
|
||||||
|
* @param state The AlarmControlPanelState.
|
||||||
|
*/
|
||||||
|
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state);
|
||||||
|
|
||||||
|
} // namespace alarm_control_panel
|
||||||
|
} // namespace esphome
|
115
esphome/components/alarm_control_panel/automation.h
Normal file
115
esphome/components/alarm_control_panel/automation.h
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "alarm_control_panel.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alarm_control_panel {
|
||||||
|
|
||||||
|
class StateTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit StateTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_state_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TriggeredTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit TriggeredTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_triggered_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ClearedTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_cleared_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit ArmAwayAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||||
|
|
||||||
|
TEMPLATABLE_VALUE(std::string, code)
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
auto call = this->alarm_control_panel_->make_call();
|
||||||
|
auto code = this->code_.optional_value(x...);
|
||||||
|
if (code.has_value()) {
|
||||||
|
call.set_code(code.value());
|
||||||
|
}
|
||||||
|
call.arm_away();
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit ArmHomeAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||||
|
|
||||||
|
TEMPLATABLE_VALUE(std::string, code)
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
auto call = this->alarm_control_panel_->make_call();
|
||||||
|
auto code = this->code_.optional_value(x...);
|
||||||
|
if (code.has_value()) {
|
||||||
|
call.set_code(code.value());
|
||||||
|
}
|
||||||
|
call.arm_home();
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class DisarmAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||||
|
|
||||||
|
TEMPLATABLE_VALUE(std::string, code)
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class PendingAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->alarm_control_panel_->make_call().pending().perform(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class TriggeredAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->alarm_control_panel_->make_call().triggered().perform(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AlarmControlPanelCondition : public Condition<Ts...> {
|
||||||
|
public:
|
||||||
|
AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {}
|
||||||
|
bool check(Ts... x) override {
|
||||||
|
return this->parent_->is_state_armed(this->parent_->get_state()) ||
|
||||||
|
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_TRIGGERED;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace alarm_control_panel
|
||||||
|
} // namespace esphome
|
|
@ -56,6 +56,8 @@ service APIConnection {
|
||||||
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||||
|
|
||||||
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
|
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
|
||||||
|
|
||||||
|
rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1454,3 +1456,63 @@ message VoiceAssistantEventResponse {
|
||||||
VoiceAssistantEvent event_type = 1;
|
VoiceAssistantEvent event_type = 1;
|
||||||
repeated VoiceAssistantEventData data = 2;
|
repeated VoiceAssistantEventData data = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== ALARM CONTROL PANEL ====================
|
||||||
|
enum AlarmControlPanelState {
|
||||||
|
ALARM_STATE_DISARMED = 0;
|
||||||
|
ALARM_STATE_ARMED_HOME = 1;
|
||||||
|
ALARM_STATE_ARMED_AWAY = 2;
|
||||||
|
ALARM_STATE_ARMED_NIGHT = 3;
|
||||||
|
ALARM_STATE_ARMED_VACATION = 4;
|
||||||
|
ALARM_STATE_ARMED_CUSTOM_BYPASS = 5;
|
||||||
|
ALARM_STATE_PENDING = 6;
|
||||||
|
ALARM_STATE_ARMING = 7;
|
||||||
|
ALARM_STATE_DISARMING = 8;
|
||||||
|
ALARM_STATE_TRIGGERED = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AlarmControlPanelStateCommand {
|
||||||
|
ALARM_CONTROL_PANEL_DISARM = 0;
|
||||||
|
ALARM_CONTROL_PANEL_ARM_AWAY = 1;
|
||||||
|
ALARM_CONTROL_PANEL_ARM_HOME = 2;
|
||||||
|
ALARM_CONTROL_PANEL_ARM_NIGHT = 3;
|
||||||
|
ALARM_CONTROL_PANEL_ARM_VACATION = 4;
|
||||||
|
ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5;
|
||||||
|
ALARM_CONTROL_PANEL_TRIGGER = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ListEntitiesAlarmControlPanelResponse {
|
||||||
|
option (id) = 94;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
uint32 supported_features = 8;
|
||||||
|
bool requires_code = 9;
|
||||||
|
bool requires_code_to_arm = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AlarmControlPanelStateResponse {
|
||||||
|
option (id) = 95;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
|
||||||
|
option (no_delay) = true;
|
||||||
|
fixed32 key = 1;
|
||||||
|
AlarmControlPanelState state = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AlarmControlPanelCommandRequest {
|
||||||
|
option (id) = 96;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
|
||||||
|
option (no_delay) = true;
|
||||||
|
fixed32 key = 1;
|
||||||
|
AlarmControlPanelStateCommand command = 2;
|
||||||
|
string code = 3;
|
||||||
|
}
|
||||||
|
|
|
@ -931,6 +931,64 @@ void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventR
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AlarmControlPanelStateResponse resp{};
|
||||||
|
resp.key = a_alarm_control_panel->get_object_id_hash();
|
||||||
|
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
|
||||||
|
return this->send_alarm_control_panel_state_response(resp);
|
||||||
|
}
|
||||||
|
bool APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
|
ListEntitiesAlarmControlPanelResponse msg;
|
||||||
|
msg.key = a_alarm_control_panel->get_object_id_hash();
|
||||||
|
msg.object_id = a_alarm_control_panel->get_object_id();
|
||||||
|
msg.name = a_alarm_control_panel->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
|
||||||
|
msg.icon = a_alarm_control_panel->get_icon();
|
||||||
|
msg.disabled_by_default = a_alarm_control_panel->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(a_alarm_control_panel->get_entity_category());
|
||||||
|
msg.supported_features = a_alarm_control_panel->get_supported_features();
|
||||||
|
msg.requires_code = a_alarm_control_panel->get_requires_code();
|
||||||
|
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
|
||||||
|
return this->send_list_entities_alarm_control_panel_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
|
||||||
|
alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key);
|
||||||
|
if (a_alarm_control_panel == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto call = a_alarm_control_panel->make_call();
|
||||||
|
switch (msg.command) {
|
||||||
|
case enums::ALARM_CONTROL_PANEL_DISARM:
|
||||||
|
call.disarm();
|
||||||
|
break;
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
|
||||||
|
call.arm_away();
|
||||||
|
break;
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_HOME:
|
||||||
|
call.arm_home();
|
||||||
|
break;
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
|
||||||
|
call.arm_night();
|
||||||
|
break;
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
|
||||||
|
call.arm_vacation();
|
||||||
|
break;
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
|
||||||
|
call.arm_custom_bypass();
|
||||||
|
break;
|
||||||
|
case enums::ALARM_CONTROL_PANEL_TRIGGER:
|
||||||
|
call.pending();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
call.set_code(msg.code);
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
|
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
|
||||||
if (this->log_subscription_ < level)
|
if (this->log_subscription_ < level)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -129,6 +129,12 @@ class APIConnection : public APIServerConnection {
|
||||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
||||||
|
bool send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
||||||
|
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
void on_disconnect_response(const DisconnectResponse &value) override;
|
void on_disconnect_response(const DisconnectResponse &value) override;
|
||||||
void on_ping_response(const PingResponse &value) override {
|
void on_ping_response(const PingResponse &value) override {
|
||||||
// we initiated ping
|
// we initiated ping
|
||||||
|
|
|
@ -433,6 +433,57 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::ALARM_STATE_DISARMED:
|
||||||
|
return "ALARM_STATE_DISARMED";
|
||||||
|
case enums::ALARM_STATE_ARMED_HOME:
|
||||||
|
return "ALARM_STATE_ARMED_HOME";
|
||||||
|
case enums::ALARM_STATE_ARMED_AWAY:
|
||||||
|
return "ALARM_STATE_ARMED_AWAY";
|
||||||
|
case enums::ALARM_STATE_ARMED_NIGHT:
|
||||||
|
return "ALARM_STATE_ARMED_NIGHT";
|
||||||
|
case enums::ALARM_STATE_ARMED_VACATION:
|
||||||
|
return "ALARM_STATE_ARMED_VACATION";
|
||||||
|
case enums::ALARM_STATE_ARMED_CUSTOM_BYPASS:
|
||||||
|
return "ALARM_STATE_ARMED_CUSTOM_BYPASS";
|
||||||
|
case enums::ALARM_STATE_PENDING:
|
||||||
|
return "ALARM_STATE_PENDING";
|
||||||
|
case enums::ALARM_STATE_ARMING:
|
||||||
|
return "ALARM_STATE_ARMING";
|
||||||
|
case enums::ALARM_STATE_DISARMING:
|
||||||
|
return "ALARM_STATE_DISARMING";
|
||||||
|
case enums::ALARM_STATE_TRIGGERED:
|
||||||
|
return "ALARM_STATE_TRIGGERED";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
template<>
|
||||||
|
const char *proto_enum_to_string<enums::AlarmControlPanelStateCommand>(enums::AlarmControlPanelStateCommand value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::ALARM_CONTROL_PANEL_DISARM:
|
||||||
|
return "ALARM_CONTROL_PANEL_DISARM";
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
|
||||||
|
return "ALARM_CONTROL_PANEL_ARM_AWAY";
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_HOME:
|
||||||
|
return "ALARM_CONTROL_PANEL_ARM_HOME";
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
|
||||||
|
return "ALARM_CONTROL_PANEL_ARM_NIGHT";
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
|
||||||
|
return "ALARM_CONTROL_PANEL_ARM_VACATION";
|
||||||
|
case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
|
||||||
|
return "ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS";
|
||||||
|
case enums::ALARM_CONTROL_PANEL_TRIGGER:
|
||||||
|
return "ALARM_CONTROL_PANEL_TRIGGER";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 2: {
|
case 2: {
|
||||||
|
@ -6436,6 +6487,217 @@ void VoiceAssistantEventResponse::dump_to(std::string &out) const {
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 6: {
|
||||||
|
this->disabled_by_default = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 7: {
|
||||||
|
this->entity_category = value.as_enum<enums::EntityCategory>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 8: {
|
||||||
|
this->supported_features = value.as_uint32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 9: {
|
||||||
|
this->requires_code = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 10: {
|
||||||
|
this->requires_code_to_arm = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesAlarmControlPanelResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->object_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->name = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->unique_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesAlarmControlPanelResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->object_id);
|
||||||
|
buffer.encode_fixed32(2, this->key);
|
||||||
|
buffer.encode_string(3, this->name);
|
||||||
|
buffer.encode_string(4, this->unique_id);
|
||||||
|
buffer.encode_string(5, this->icon);
|
||||||
|
buffer.encode_bool(6, this->disabled_by_default);
|
||||||
|
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
|
||||||
|
buffer.encode_uint32(8, this->supported_features);
|
||||||
|
buffer.encode_bool(9, this->requires_code);
|
||||||
|
buffer.encode_bool(10, this->requires_code_to_arm);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("ListEntitiesAlarmControlPanelResponse {\n");
|
||||||
|
out.append(" object_id: ");
|
||||||
|
out.append("'").append(this->object_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%u", this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" name: ");
|
||||||
|
out.append("'").append(this->name).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" unique_id: ");
|
||||||
|
out.append("'").append(this->unique_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" disabled_by_default: ");
|
||||||
|
out.append(YESNO(this->disabled_by_default));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" entity_category: ");
|
||||||
|
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" supported_features: ");
|
||||||
|
sprintf(buffer, "%u", this->supported_features);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" requires_code: ");
|
||||||
|
out.append(YESNO(this->requires_code));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" requires_code_to_arm: ");
|
||||||
|
out.append(YESNO(this->requires_code_to_arm));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool AlarmControlPanelStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->state = value.as_enum<enums::AlarmControlPanelState>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool AlarmControlPanelStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_enum<enums::AlarmControlPanelState>(2, this->state);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void AlarmControlPanelStateResponse::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("AlarmControlPanelStateResponse {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%u", this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" state: ");
|
||||||
|
out.append(proto_enum_to_string<enums::AlarmControlPanelState>(this->state));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool AlarmControlPanelCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->command = value.as_enum<enums::AlarmControlPanelStateCommand>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool AlarmControlPanelCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 3: {
|
||||||
|
this->code = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AlarmControlPanelCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_enum<enums::AlarmControlPanelStateCommand>(2, this->command);
|
||||||
|
buffer.encode_string(3, this->code);
|
||||||
|
}
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void AlarmControlPanelCommandRequest::dump_to(std::string &out) const {
|
||||||
|
__attribute__((unused)) char buffer[64];
|
||||||
|
out.append("AlarmControlPanelCommandRequest {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%u", this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" command: ");
|
||||||
|
out.append(proto_enum_to_string<enums::AlarmControlPanelStateCommand>(this->command));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" code: ");
|
||||||
|
out.append("'").append(this->code).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -176,6 +176,27 @@ enum VoiceAssistantEvent : uint32_t {
|
||||||
VOICE_ASSISTANT_TTS_START = 7,
|
VOICE_ASSISTANT_TTS_START = 7,
|
||||||
VOICE_ASSISTANT_TTS_END = 8,
|
VOICE_ASSISTANT_TTS_END = 8,
|
||||||
};
|
};
|
||||||
|
enum AlarmControlPanelState : uint32_t {
|
||||||
|
ALARM_STATE_DISARMED = 0,
|
||||||
|
ALARM_STATE_ARMED_HOME = 1,
|
||||||
|
ALARM_STATE_ARMED_AWAY = 2,
|
||||||
|
ALARM_STATE_ARMED_NIGHT = 3,
|
||||||
|
ALARM_STATE_ARMED_VACATION = 4,
|
||||||
|
ALARM_STATE_ARMED_CUSTOM_BYPASS = 5,
|
||||||
|
ALARM_STATE_PENDING = 6,
|
||||||
|
ALARM_STATE_ARMING = 7,
|
||||||
|
ALARM_STATE_DISARMING = 8,
|
||||||
|
ALARM_STATE_TRIGGERED = 9,
|
||||||
|
};
|
||||||
|
enum AlarmControlPanelStateCommand : uint32_t {
|
||||||
|
ALARM_CONTROL_PANEL_DISARM = 0,
|
||||||
|
ALARM_CONTROL_PANEL_ARM_AWAY = 1,
|
||||||
|
ALARM_CONTROL_PANEL_ARM_HOME = 2,
|
||||||
|
ALARM_CONTROL_PANEL_ARM_NIGHT = 3,
|
||||||
|
ALARM_CONTROL_PANEL_ARM_VACATION = 4,
|
||||||
|
ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5,
|
||||||
|
ALARM_CONTROL_PANEL_TRIGGER = 6,
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace enums
|
} // namespace enums
|
||||||
|
|
||||||
|
@ -1680,6 +1701,56 @@ class VoiceAssistantEventResponse : 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 ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
std::string object_id{};
|
||||||
|
uint32_t key{0};
|
||||||
|
std::string name{};
|
||||||
|
std::string unique_id{};
|
||||||
|
std::string icon{};
|
||||||
|
bool disabled_by_default{false};
|
||||||
|
enums::EntityCategory entity_category{};
|
||||||
|
uint32_t supported_features{0};
|
||||||
|
bool requires_code{false};
|
||||||
|
bool requires_code_to_arm{false};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class AlarmControlPanelStateResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
enums::AlarmControlPanelState state{};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class AlarmControlPanelCommandRequest : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
enums::AlarmControlPanelStateCommand command{};
|
||||||
|
std::string code{};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -476,6 +476,25 @@ bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantR
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
|
||||||
|
const ListEntitiesAlarmControlPanelResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_list_entities_alarm_control_panel_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<ListEntitiesAlarmControlPanelResponse>(msg, 94);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool APIServerConnectionBase::send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_alarm_control_panel_state_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<AlarmControlPanelStateResponse>(msg, 95);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
#endif
|
||||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||||
switch (msg_type) {
|
switch (msg_type) {
|
||||||
case 1: {
|
case 1: {
|
||||||
|
@ -883,6 +902,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||||
ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_event_response(msg);
|
this->on_voice_assistant_event_response(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 96: {
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
AlarmControlPanelCommandRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_alarm_control_panel_command_request: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_alarm_control_panel_command_request(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1295,6 +1325,19 @@ void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVo
|
||||||
this->subscribe_voice_assistant(msg);
|
this->subscribe_voice_assistant(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->alarm_control_panel_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -239,6 +239,15 @@ class APIServerConnectionBase : public ProtoService {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
|
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||||
|
@ -324,6 +333,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
void on_hello_request(const HelloRequest &msg) override;
|
void on_hello_request(const HelloRequest &msg) override;
|
||||||
|
@ -405,6 +417,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
|
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|
|
@ -338,5 +338,14 @@ void APIServer::stop_voice_assistant() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
|
for (auto &c : this->clients_)
|
||||||
|
c->send_alarm_control_panel_state(obj);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -85,6 +85,10 @@ class APIServer : public Component, public Controller {
|
||||||
void stop_voice_assistant();
|
void stop_voice_assistant();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool is_connected() const;
|
bool is_connected() const;
|
||||||
|
|
||||||
struct HomeAssistantStateSubscription {
|
struct HomeAssistantStateSubscription {
|
||||||
|
|
|
@ -69,6 +69,11 @@ bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_play
|
||||||
return this->client_->send_media_player_info(media_player);
|
return this->client_->send_media_player_info(media_player);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
|
return this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -54,6 +54,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||||
#endif
|
#endif
|
||||||
bool on_end() override;
|
bool on_end() override;
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,11 @@ bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_play
|
||||||
return this->client_->send_media_player_state(media_player);
|
return this->client_->send_media_player_state(media_player);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool InitialStateIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
|
return this->client_->send_alarm_control_panel_state(a_alarm_control_panel);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
|
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|
|
@ -51,6 +51,9 @@ class InitialStateIterator : public ComponentIterator {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
APIConnection *client_;
|
APIConnection *client_;
|
||||||
|
|
|
@ -15,6 +15,25 @@ static const char *const TAG = "display";
|
||||||
const Color COLOR_OFF(0, 0, 0, 255);
|
const Color COLOR_OFF(0, 0, 0, 255);
|
||||||
const Color COLOR_ON(255, 255, 255, 255);
|
const Color COLOR_ON(255, 255, 255, 255);
|
||||||
|
|
||||||
|
static int image_type_to_bpp(ImageType type) {
|
||||||
|
switch (type) {
|
||||||
|
case IMAGE_TYPE_BINARY:
|
||||||
|
return 1;
|
||||||
|
case IMAGE_TYPE_GRAYSCALE:
|
||||||
|
return 8;
|
||||||
|
case IMAGE_TYPE_RGB565:
|
||||||
|
return 16;
|
||||||
|
case IMAGE_TYPE_RGB24:
|
||||||
|
return 24;
|
||||||
|
case IMAGE_TYPE_RGBA:
|
||||||
|
return 32;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; }
|
||||||
|
|
||||||
void Rect::expand(int16_t horizontal, int16_t vertical) {
|
void Rect::expand(int16_t horizontal, int16_t vertical) {
|
||||||
if (this->is_set() && (this->w >= (-2 * horizontal)) && (this->h >= (-2 * vertical))) {
|
if (this->is_set() && (this->w >= (-2 * horizontal)) && (this->h >= (-2 * vertical))) {
|
||||||
this->x = this->x - horizontal;
|
this->x = this->x - horizontal;
|
||||||
|
@ -306,63 +325,8 @@ void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign al
|
||||||
this->print(x, y, font, color, align, buffer);
|
this->print(x, y, font, color, align, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayBuffer::image(int x, int y, Image *image, Color color_on, Color color_off) {
|
void DisplayBuffer::image(int x, int y, BaseImage *image, Color color_on, Color color_off) {
|
||||||
bool transparent = image->has_transparency();
|
image->draw(x, y, this, color_on, color_off);
|
||||||
|
|
||||||
switch (image->get_type()) {
|
|
||||||
case IMAGE_TYPE_BINARY: {
|
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
|
||||||
if (image->get_pixel(img_x, img_y)) {
|
|
||||||
this->draw_pixel_at(x + img_x, y + img_y, color_on);
|
|
||||||
} else if (!transparent) {
|
|
||||||
this->draw_pixel_at(x + img_x, y + img_y, color_off);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case IMAGE_TYPE_GRAYSCALE:
|
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
|
||||||
auto color = image->get_grayscale_pixel(img_x, img_y);
|
|
||||||
if (color.w >= 0x80) {
|
|
||||||
this->draw_pixel_at(x + img_x, y + img_y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IMAGE_TYPE_RGB565:
|
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
|
||||||
auto color = image->get_rgb565_pixel(img_x, img_y);
|
|
||||||
if (color.w >= 0x80) {
|
|
||||||
this->draw_pixel_at(x + img_x, y + img_y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IMAGE_TYPE_RGB24:
|
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
|
||||||
auto color = image->get_color_pixel(img_x, img_y);
|
|
||||||
if (color.w >= 0x80) {
|
|
||||||
this->draw_pixel_at(x + img_x, y + img_y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IMAGE_TYPE_RGBA:
|
|
||||||
for (int img_x = 0; img_x < image->get_width(); img_x++) {
|
|
||||||
for (int img_y = 0; img_y < image->get_height(); img_y++) {
|
|
||||||
auto color = image->get_rgba_pixel(img_x, img_y);
|
|
||||||
if (color.w >= 0x80) {
|
|
||||||
this->draw_pixel_at(x + img_x, y + img_y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_GRAPH
|
#ifdef USE_GRAPH
|
||||||
|
@ -637,23 +601,91 @@ Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : basel
|
||||||
glyphs_.emplace_back(&data[i]);
|
glyphs_.emplace_back(&data[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::get_pixel(int x, int y) const {
|
void Image::draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) {
|
||||||
|
switch (type_) {
|
||||||
|
case IMAGE_TYPE_BINARY: {
|
||||||
|
for (int img_x = 0; img_x < width_; img_x++) {
|
||||||
|
for (int img_y = 0; img_y < height_; img_y++) {
|
||||||
|
if (this->get_binary_pixel_(img_x, img_y)) {
|
||||||
|
display->draw_pixel_at(x + img_x, y + img_y, color_on);
|
||||||
|
} else if (!this->transparent_) {
|
||||||
|
display->draw_pixel_at(x + img_x, y + img_y, color_off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IMAGE_TYPE_GRAYSCALE:
|
||||||
|
for (int img_x = 0; img_x < width_; img_x++) {
|
||||||
|
for (int img_y = 0; img_y < height_; img_y++) {
|
||||||
|
auto color = this->get_grayscale_pixel_(img_x, img_y);
|
||||||
|
if (color.w >= 0x80) {
|
||||||
|
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_RGB565:
|
||||||
|
for (int img_x = 0; img_x < width_; img_x++) {
|
||||||
|
for (int img_y = 0; img_y < height_; img_y++) {
|
||||||
|
auto color = this->get_rgb565_pixel_(img_x, img_y);
|
||||||
|
if (color.w >= 0x80) {
|
||||||
|
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_RGB24:
|
||||||
|
for (int img_x = 0; img_x < width_; img_x++) {
|
||||||
|
for (int img_y = 0; img_y < height_; img_y++) {
|
||||||
|
auto color = this->get_rgb24_pixel_(img_x, img_y);
|
||||||
|
if (color.w >= 0x80) {
|
||||||
|
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IMAGE_TYPE_RGBA:
|
||||||
|
for (int img_x = 0; img_x < width_; img_x++) {
|
||||||
|
for (int img_y = 0; img_y < height_; img_y++) {
|
||||||
|
auto color = this->get_rgba_pixel_(img_x, img_y);
|
||||||
|
if (color.w >= 0x80) {
|
||||||
|
display->draw_pixel_at(x + img_x, y + img_y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const {
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
||||||
return false;
|
return color_off;
|
||||||
|
switch (this->type_) {
|
||||||
|
case IMAGE_TYPE_BINARY:
|
||||||
|
return this->get_binary_pixel_(x, y) ? color_on : color_off;
|
||||||
|
case IMAGE_TYPE_GRAYSCALE:
|
||||||
|
return this->get_grayscale_pixel_(x, y);
|
||||||
|
case IMAGE_TYPE_RGB565:
|
||||||
|
return this->get_rgb565_pixel_(x, y);
|
||||||
|
case IMAGE_TYPE_RGB24:
|
||||||
|
return this->get_rgb24_pixel_(x, y);
|
||||||
|
case IMAGE_TYPE_RGBA:
|
||||||
|
return this->get_rgba_pixel_(x, y);
|
||||||
|
default:
|
||||||
|
return color_off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Image::get_binary_pixel_(int x, int y) const {
|
||||||
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
||||||
const uint32_t pos = x + y * width_8;
|
const uint32_t pos = x + y * width_8;
|
||||||
return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
|
return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
|
||||||
}
|
}
|
||||||
Color Image::get_rgba_pixel(int x, int y) const {
|
Color Image::get_rgba_pixel_(int x, int y) const {
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_) * 4;
|
const uint32_t pos = (x + y * this->width_) * 4;
|
||||||
return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
||||||
progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3));
|
progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3));
|
||||||
}
|
}
|
||||||
Color Image::get_color_pixel(int x, int y) const {
|
Color Image::get_rgb24_pixel_(int x, int y) const {
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_) * 3;
|
const uint32_t pos = (x + y * this->width_) * 3;
|
||||||
Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
||||||
progmem_read_byte(this->data_start_ + pos + 2));
|
progmem_read_byte(this->data_start_ + pos + 2));
|
||||||
|
@ -666,9 +698,7 @@ Color Image::get_color_pixel(int x, int y) const {
|
||||||
}
|
}
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
Color Image::get_rgb565_pixel(int x, int y) const {
|
Color Image::get_rgb565_pixel_(int x, int y) const {
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_) * 2;
|
const uint32_t pos = (x + y * this->width_) * 2;
|
||||||
uint16_t rgb565 =
|
uint16_t rgb565 =
|
||||||
progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1);
|
progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1);
|
||||||
|
@ -684,9 +714,7 @@ Color Image::get_rgb565_pixel(int x, int y) const {
|
||||||
}
|
}
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
Color Image::get_grayscale_pixel(int x, int y) const {
|
Color Image::get_grayscale_pixel_(int x, int y) const {
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_);
|
const uint32_t pos = (x + y * this->width_);
|
||||||
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
||||||
uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF;
|
uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF;
|
||||||
|
@ -697,80 +725,10 @@ int Image::get_height() const { return this->height_; }
|
||||||
ImageType Image::get_type() const { return this->type_; }
|
ImageType Image::get_type() const { return this->type_; }
|
||||||
Image::Image(const uint8_t *data_start, int width, int height, ImageType type)
|
Image::Image(const uint8_t *data_start, int width, int height, ImageType type)
|
||||||
: width_(width), height_(height), type_(type), data_start_(data_start) {}
|
: width_(width), height_(height), type_(type), data_start_(data_start) {}
|
||||||
int Image::get_current_frame() const { return 0; }
|
|
||||||
|
|
||||||
bool Animation::get_pixel(int x, int y) const {
|
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return false;
|
|
||||||
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
|
|
||||||
const uint32_t frame_index = this->height_ * width_8 * this->current_frame_;
|
|
||||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
|
||||||
return false;
|
|
||||||
const uint32_t pos = x + y * width_8 + frame_index;
|
|
||||||
return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
|
|
||||||
}
|
|
||||||
Color Animation::get_rgba_pixel(int x, int y) const {
|
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
|
|
||||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_ + frame_index) * 4;
|
|
||||||
return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
|
||||||
progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3));
|
|
||||||
}
|
|
||||||
Color Animation::get_color_pixel(int x, int y) const {
|
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
|
|
||||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_ + frame_index) * 3;
|
|
||||||
Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1),
|
|
||||||
progmem_read_byte(this->data_start_ + pos + 2));
|
|
||||||
if (color.b == 1 && color.r == 0 && color.g == 0 && transparent_) {
|
|
||||||
// (0, 0, 1) has been defined as transparent color for non-alpha images.
|
|
||||||
// putting blue == 1 as a first condition for performance reasons (least likely value to short-cut the if)
|
|
||||||
color.w = 0;
|
|
||||||
} else {
|
|
||||||
color.w = 0xFF;
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
Color Animation::get_rgb565_pixel(int x, int y) const {
|
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
|
|
||||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_ + frame_index) * 2;
|
|
||||||
uint16_t rgb565 =
|
|
||||||
progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1);
|
|
||||||
auto r = (rgb565 & 0xF800) >> 11;
|
|
||||||
auto g = (rgb565 & 0x07E0) >> 5;
|
|
||||||
auto b = rgb565 & 0x001F;
|
|
||||||
Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
|
|
||||||
if (rgb565 == 0x0020 && transparent_) {
|
|
||||||
// darkest green has been defined as transparent color for transparent RGB565 images.
|
|
||||||
color.w = 0;
|
|
||||||
} else {
|
|
||||||
color.w = 0xFF;
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
Color Animation::get_grayscale_pixel(int x, int y) const {
|
|
||||||
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
|
|
||||||
if (frame_index >= (uint32_t) (this->width_ * this->height_ * this->animation_frame_count_))
|
|
||||||
return Color::BLACK;
|
|
||||||
const uint32_t pos = (x + y * this->width_ + frame_index);
|
|
||||||
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
|
|
||||||
uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF;
|
|
||||||
return Color(gray, gray, gray, alpha);
|
|
||||||
}
|
|
||||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type)
|
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type)
|
||||||
: Image(data_start, width, height, type),
|
: Image(data_start, width, height, type),
|
||||||
|
animation_data_start_(data_start),
|
||||||
current_frame_(0),
|
current_frame_(0),
|
||||||
animation_frame_count_(animation_frame_count),
|
animation_frame_count_(animation_frame_count),
|
||||||
loop_start_frame_(0),
|
loop_start_frame_(0),
|
||||||
|
@ -797,12 +755,16 @@ void Animation::next_frame() {
|
||||||
this->loop_current_iteration_ = 1;
|
this->loop_current_iteration_ = 1;
|
||||||
this->current_frame_ = 0;
|
this->current_frame_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->update_data_start_();
|
||||||
}
|
}
|
||||||
void Animation::prev_frame() {
|
void Animation::prev_frame() {
|
||||||
this->current_frame_--;
|
this->current_frame_--;
|
||||||
if (this->current_frame_ < 0) {
|
if (this->current_frame_ < 0) {
|
||||||
this->current_frame_ = this->animation_frame_count_ - 1;
|
this->current_frame_ = this->animation_frame_count_ - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->update_data_start_();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::set_frame(int frame) {
|
void Animation::set_frame(int frame) {
|
||||||
|
@ -815,6 +777,13 @@ void Animation::set_frame(int frame) {
|
||||||
this->current_frame_ = this->animation_frame_count_ - abs_frame;
|
this->current_frame_ = this->animation_frame_count_ - abs_frame;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->update_data_start_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::update_data_start_() {
|
||||||
|
const uint32_t image_size = image_type_to_width_stride(this->width_, this->type_) * this->height_;
|
||||||
|
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
|
DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {}
|
||||||
|
|
|
@ -123,8 +123,8 @@ class Rect {
|
||||||
void info(const std::string &prefix = "rect info:");
|
void info(const std::string &prefix = "rect info:");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BaseImage;
|
||||||
class Font;
|
class Font;
|
||||||
class Image;
|
|
||||||
class DisplayBuffer;
|
class DisplayBuffer;
|
||||||
class DisplayPage;
|
class DisplayPage;
|
||||||
class DisplayOnPageChangeTrigger;
|
class DisplayOnPageChangeTrigger;
|
||||||
|
@ -315,7 +315,7 @@ class DisplayBuffer {
|
||||||
* @param color_on The color to replace in binary images for the on bits.
|
* @param color_on The color to replace in binary images for the on bits.
|
||||||
* @param color_off The color to replace in binary images for the off bits.
|
* @param color_off The color to replace in binary images for the off bits.
|
||||||
*/
|
*/
|
||||||
void image(int x, int y, Image *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
void image(int x, int y, BaseImage *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF);
|
||||||
|
|
||||||
#ifdef USE_GRAPH
|
#ifdef USE_GRAPH
|
||||||
/** Draw the `graph` with the top-left corner at [x,y] to the screen.
|
/** Draw the `graph` with the top-left corner at [x,y] to the screen.
|
||||||
|
@ -529,24 +529,33 @@ class Font {
|
||||||
int height_;
|
int height_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Image {
|
class BaseImage {
|
||||||
|
public:
|
||||||
|
virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0;
|
||||||
|
virtual int get_width() const = 0;
|
||||||
|
virtual int get_height() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Image : public BaseImage {
|
||||||
public:
|
public:
|
||||||
Image(const uint8_t *data_start, int width, int height, ImageType type);
|
Image(const uint8_t *data_start, int width, int height, ImageType type);
|
||||||
virtual bool get_pixel(int x, int y) const;
|
Color get_pixel(int x, int y, Color color_on = COLOR_ON, Color color_off = COLOR_OFF) const;
|
||||||
virtual Color get_color_pixel(int x, int y) const;
|
int get_width() const override;
|
||||||
virtual Color get_rgba_pixel(int x, int y) const;
|
int get_height() const override;
|
||||||
virtual Color get_rgb565_pixel(int x, int y) const;
|
|
||||||
virtual Color get_grayscale_pixel(int x, int y) const;
|
|
||||||
int get_width() const;
|
|
||||||
int get_height() const;
|
|
||||||
ImageType get_type() const;
|
ImageType get_type() const;
|
||||||
|
|
||||||
virtual int get_current_frame() const;
|
void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) override;
|
||||||
|
|
||||||
void set_transparency(bool transparent) { transparent_ = transparent; }
|
void set_transparency(bool transparent) { transparent_ = transparent; }
|
||||||
bool has_transparency() const { return transparent_; }
|
bool has_transparency() const { return transparent_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool get_binary_pixel_(int x, int y) const;
|
||||||
|
Color get_rgb24_pixel_(int x, int y) const;
|
||||||
|
Color get_rgba_pixel_(int x, int y) const;
|
||||||
|
Color get_rgb565_pixel_(int x, int y) const;
|
||||||
|
Color get_grayscale_pixel_(int x, int y) const;
|
||||||
|
|
||||||
int width_;
|
int width_;
|
||||||
int height_;
|
int height_;
|
||||||
ImageType type_;
|
ImageType type_;
|
||||||
|
@ -557,14 +566,9 @@ class Image {
|
||||||
class Animation : public Image {
|
class Animation : public Image {
|
||||||
public:
|
public:
|
||||||
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type);
|
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type);
|
||||||
bool get_pixel(int x, int y) const override;
|
|
||||||
Color get_color_pixel(int x, int y) const override;
|
|
||||||
Color get_rgba_pixel(int x, int y) const override;
|
|
||||||
Color get_rgb565_pixel(int x, int y) const override;
|
|
||||||
Color get_grayscale_pixel(int x, int y) const override;
|
|
||||||
|
|
||||||
uint32_t get_animation_frame_count() const;
|
uint32_t get_animation_frame_count() const;
|
||||||
int get_current_frame() const override;
|
int get_current_frame() const;
|
||||||
void next_frame();
|
void next_frame();
|
||||||
void prev_frame();
|
void prev_frame();
|
||||||
|
|
||||||
|
@ -577,6 +581,9 @@ class Animation : public Image {
|
||||||
void set_loop(uint32_t start_frame, uint32_t end_frame, int count);
|
void set_loop(uint32_t start_frame, uint32_t end_frame, int count);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void update_data_start_();
|
||||||
|
|
||||||
|
const uint8_t *animation_data_start_;
|
||||||
int current_frame_;
|
int current_frame_;
|
||||||
uint32_t animation_frame_count_;
|
uint32_t animation_frame_count_;
|
||||||
uint32_t loop_start_frame_;
|
uint32_t loop_start_frame_;
|
||||||
|
|
|
@ -27,6 +27,7 @@ i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t")
|
||||||
CONF_MUTE_PIN = "mute_pin"
|
CONF_MUTE_PIN = "mute_pin"
|
||||||
CONF_AUDIO_ID = "audio_id"
|
CONF_AUDIO_ID = "audio_id"
|
||||||
CONF_DAC_TYPE = "dac_type"
|
CONF_DAC_TYPE = "dac_type"
|
||||||
|
CONF_I2S_COMM_FMT = "i2s_comm_fmt"
|
||||||
|
|
||||||
INTERNAL_DAC_OPTIONS = {
|
INTERNAL_DAC_OPTIONS = {
|
||||||
"left": i2s_dac_mode_t.I2S_DAC_CHANNEL_LEFT_EN,
|
"left": i2s_dac_mode_t.I2S_DAC_CHANNEL_LEFT_EN,
|
||||||
|
@ -38,6 +39,8 @@ EXTERNAL_DAC_OPTIONS = ["mono", "stereo"]
|
||||||
|
|
||||||
NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2]
|
NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2]
|
||||||
|
|
||||||
|
I2C_COMM_FMT_OPTIONS = ["lsb", "msb"]
|
||||||
|
|
||||||
|
|
||||||
def validate_esp32_variant(config):
|
def validate_esp32_variant(config):
|
||||||
if config[CONF_DAC_TYPE] != "internal":
|
if config[CONF_DAC_TYPE] != "internal":
|
||||||
|
@ -69,6 +72,9 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_MODE, default="mono"): cv.one_of(
|
cv.Optional(CONF_MODE, default="mono"): cv.one_of(
|
||||||
*EXTERNAL_DAC_OPTIONS, lower=True
|
*EXTERNAL_DAC_OPTIONS, lower=True
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_I2S_COMM_FMT, default="msb"): cv.one_of(
|
||||||
|
*I2C_COMM_FMT_OPTIONS, lower=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
},
|
},
|
||||||
|
@ -94,6 +100,7 @@ async def to_code(config):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_MUTE_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_MUTE_PIN])
|
||||||
cg.add(var.set_mute_pin(pin))
|
cg.add(var.set_mute_pin(pin))
|
||||||
cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
|
cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
|
||||||
|
cg.add(var.set_i2s_comm_fmt_lsb(config[CONF_I2S_COMM_FMT] == "lsb"))
|
||||||
|
|
||||||
cg.add_library("WiFiClientSecure", None)
|
cg.add_library("WiFiClientSecure", None)
|
||||||
cg.add_library("HTTPClient", None)
|
cg.add_library("HTTPClient", None)
|
||||||
|
|
|
@ -148,6 +148,7 @@ void I2SAudioMediaPlayer::start_() {
|
||||||
pin_config.data_out_num = this->dout_pin_;
|
pin_config.data_out_num = this->dout_pin_;
|
||||||
i2s_set_pin(this->parent_->get_port(), &pin_config);
|
i2s_set_pin(this->parent_->get_port(), &pin_config);
|
||||||
|
|
||||||
|
this->audio_->setI2SCommFMT_LSB(this->i2s_comm_fmt_lsb_);
|
||||||
this->audio_->forceMono(this->external_dac_channels_ == 1);
|
this->audio_->forceMono(this->external_dac_channels_ == 1);
|
||||||
if (this->mute_pin_ != nullptr) {
|
if (this->mute_pin_ != nullptr) {
|
||||||
this->mute_pin_->setup();
|
this->mute_pin_->setup();
|
||||||
|
|
|
@ -39,6 +39,8 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer,
|
||||||
#endif
|
#endif
|
||||||
void set_external_dac_channels(uint8_t channels) { this->external_dac_channels_ = channels; }
|
void set_external_dac_channels(uint8_t channels) { this->external_dac_channels_ = channels; }
|
||||||
|
|
||||||
|
void set_i2s_comm_fmt_lsb(bool lsb) { this->i2s_comm_fmt_lsb_ = lsb; }
|
||||||
|
|
||||||
media_player::MediaPlayerTraits get_traits() override;
|
media_player::MediaPlayerTraits get_traits() override;
|
||||||
|
|
||||||
bool is_muted() const override { return this->muted_; }
|
bool is_muted() const override { return this->muted_; }
|
||||||
|
@ -71,6 +73,8 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer,
|
||||||
#endif
|
#endif
|
||||||
uint8_t external_dac_channels_;
|
uint8_t external_dac_channels_;
|
||||||
|
|
||||||
|
bool i2s_comm_fmt_lsb_;
|
||||||
|
|
||||||
HighFrequencyLoopRequester high_freq_;
|
HighFrequencyLoopRequester high_freq_;
|
||||||
|
|
||||||
optional<std::string> current_url_{};
|
optional<std::string> current_url_{};
|
||||||
|
|
|
@ -44,6 +44,7 @@ MODELS = {
|
||||||
"ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
|
"ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
|
||||||
"ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
|
"ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
|
||||||
"ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI),
|
"ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI),
|
||||||
|
"S3BOX_LITE": ili9XXX_ns.class_("ILI9XXXS3BoxLite", ili9XXXSPI),
|
||||||
}
|
}
|
||||||
|
|
||||||
COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
|
COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
|
||||||
|
|
|
@ -421,5 +421,17 @@ void ILI9XXXST7796::initialize() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 24_TFT rotated display
|
||||||
|
void ILI9XXXS3BoxLite::initialize() {
|
||||||
|
this->init_lcd_(INITCMD_S3BOXLITE);
|
||||||
|
if (this->width_ == 0) {
|
||||||
|
this->width_ = 320;
|
||||||
|
}
|
||||||
|
if (this->height_ == 0) {
|
||||||
|
this->height_ = 240;
|
||||||
|
}
|
||||||
|
this->invert_display_(true);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ili9xxx
|
} // namespace ili9xxx
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -134,5 +134,10 @@ class ILI9XXXST7796 : public ILI9XXXDisplay {
|
||||||
void initialize() override;
|
void initialize() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ILI9XXXS3BoxLite : public ILI9XXXDisplay {
|
||||||
|
protected:
|
||||||
|
void initialize() override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ili9xxx
|
} // namespace ili9xxx
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -169,6 +169,36 @@ static const uint8_t PROGMEM INITCMD_ST7796[] = {
|
||||||
0x00 // End of list
|
0x00 // End of list
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = {
|
||||||
|
0xEF, 3, 0x03, 0x80, 0x02,
|
||||||
|
0xCF, 3, 0x00, 0xC1, 0x30,
|
||||||
|
0xED, 4, 0x64, 0x03, 0x12, 0x81,
|
||||||
|
0xE8, 3, 0x85, 0x00, 0x78,
|
||||||
|
0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02,
|
||||||
|
0xF7, 1, 0x20,
|
||||||
|
0xEA, 2, 0x00, 0x00,
|
||||||
|
ILI9XXX_PWCTR1 , 1, 0x23, // Power control VRH[5:0]
|
||||||
|
ILI9XXX_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0]
|
||||||
|
ILI9XXX_VMCTR1 , 2, 0x3e, 0x28, // VCM control
|
||||||
|
ILI9XXX_VMCTR2 , 1, 0x86, // VCM control2
|
||||||
|
ILI9XXX_MADCTL , 1, 0x40, // Memory Access Control
|
||||||
|
ILI9XXX_VSCRSADD, 1, 0x00, // Vertical scroll zero
|
||||||
|
ILI9XXX_PIXFMT , 1, 0x55,
|
||||||
|
ILI9XXX_FRMCTR1 , 2, 0x00, 0x18,
|
||||||
|
ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control
|
||||||
|
0xF2, 1, 0x00, // 3Gamma Function Disable
|
||||||
|
ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected
|
||||||
|
ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
|
||||||
|
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
|
||||||
|
0x0E, 0x09, 0x00,
|
||||||
|
ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
|
||||||
|
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C,
|
||||||
|
0x31, 0x36, 0x0F,
|
||||||
|
ILI9XXX_SLPOUT , 0x80, // Exit Sleep
|
||||||
|
ILI9XXX_DISPON , 0x80, // Display on
|
||||||
|
0x00 // End of list
|
||||||
|
};
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
} // namespace ili9xxx
|
} // namespace ili9xxx
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -199,6 +199,27 @@ IMAGE_SCHEMA = cv.Schema(
|
||||||
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA)
|
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
def load_svg_image(file: str, resize: tuple[int, int]):
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
# This import is only needed in case of SVG images; adding it
|
||||||
|
# to the top would force configurations not using SVG to also have it
|
||||||
|
# installed for no reason.
|
||||||
|
from cairosvg import svg2png
|
||||||
|
|
||||||
|
if resize:
|
||||||
|
req_width, req_height = resize
|
||||||
|
svg_image = svg2png(
|
||||||
|
url=file,
|
||||||
|
output_width=req_width,
|
||||||
|
output_height=req_height,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
svg_image = svg2png(url=file)
|
||||||
|
|
||||||
|
return Image.open(io.BytesIO(svg_image))
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
@ -206,30 +227,20 @@ async def to_code(config):
|
||||||
|
|
||||||
if conf_file[CONF_SOURCE] == SOURCE_LOCAL:
|
if conf_file[CONF_SOURCE] == SOURCE_LOCAL:
|
||||||
path = CORE.relative_config_path(conf_file[CONF_PATH])
|
path = CORE.relative_config_path(conf_file[CONF_PATH])
|
||||||
|
|
||||||
|
elif conf_file[CONF_SOURCE] == SOURCE_MDI:
|
||||||
|
path = _compute_local_icon_path(conf_file).as_posix()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
resize = config.get(CONF_RESIZE)
|
||||||
|
if path.lower().endswith(".svg"):
|
||||||
|
image = load_svg_image(path, resize)
|
||||||
|
else:
|
||||||
image = Image.open(path)
|
image = Image.open(path)
|
||||||
|
if resize:
|
||||||
|
image.thumbnail(resize)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise core.EsphomeError(f"Could not load image file {path}: {e}")
|
raise core.EsphomeError(f"Could not load image file {path}: {e}")
|
||||||
if CONF_RESIZE in config:
|
|
||||||
image.thumbnail(config[CONF_RESIZE])
|
|
||||||
elif conf_file[CONF_SOURCE] == SOURCE_MDI:
|
|
||||||
# Those imports are only needed in case of MDI images; adding them
|
|
||||||
# to the top would force configurations not using MDI to also have them
|
|
||||||
# installed for no reason.
|
|
||||||
from cairosvg import svg2png
|
|
||||||
|
|
||||||
svg_file = _compute_local_icon_path(conf_file)
|
|
||||||
if CONF_RESIZE in config:
|
|
||||||
req_width, req_height = config[CONF_RESIZE]
|
|
||||||
svg_image = svg2png(
|
|
||||||
url=svg_file.as_posix(),
|
|
||||||
output_width=req_width,
|
|
||||||
output_height=req_height,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
svg_image = svg2png(url=svg_file.as_posix())
|
|
||||||
|
|
||||||
image = Image.open(io.BytesIO(svg_image))
|
|
||||||
|
|
||||||
width, height = image.size
|
width, height = image.size
|
||||||
|
|
||||||
|
|
|
@ -278,7 +278,9 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
b = pixels[7 - col];
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
|
b |= ((pixels[7 - col] >> i) & 1) << (7 - i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// send this byte to display at selected chip
|
// send this byte to display at selected chip
|
||||||
if (this->invert_) {
|
if (this->invert_) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ from esphome.const import CONF_ID
|
||||||
AUTO_LOAD = ["pn532"]
|
AUTO_LOAD = ["pn532"]
|
||||||
CODEOWNERS = ["@OttoWinter", "@jesserockz"]
|
CODEOWNERS = ["@OttoWinter", "@jesserockz"]
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
pn532_i2c_ns = cg.esphome_ns.namespace("pn532_i2c")
|
pn532_i2c_ns = cg.esphome_ns.namespace("pn532_i2c")
|
||||||
PN532I2C = pn532_i2c_ns.class_("PN532I2C", pn532.PN532, i2c.I2CDevice)
|
PN532I2C = pn532_i2c_ns.class_("PN532I2C", pn532.PN532, i2c.I2CDevice)
|
||||||
|
|
123
esphome/components/template/alarm_control_panel/__init__.py
Normal file
123
esphome/components/template/alarm_control_panel/__init__.py
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import (
|
||||||
|
binary_sensor,
|
||||||
|
alarm_control_panel,
|
||||||
|
)
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_BINARY_SENSORS,
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_RESTORE_MODE,
|
||||||
|
)
|
||||||
|
from .. import template_ns
|
||||||
|
|
||||||
|
CODEOWNERS = ["@grahambrown11"]
|
||||||
|
|
||||||
|
CONF_CODES = "codes"
|
||||||
|
CONF_BYPASS_ARMED_HOME = "bypass_armed_home"
|
||||||
|
CONF_REQUIRES_CODE_TO_ARM = "requires_code_to_arm"
|
||||||
|
CONF_ARMING_HOME_TIME = "arming_home_time"
|
||||||
|
CONF_ARMING_AWAY_TIME = "arming_away_time"
|
||||||
|
CONF_PENDING_TIME = "pending_time"
|
||||||
|
CONF_TRIGGER_TIME = "trigger_time"
|
||||||
|
|
||||||
|
FLAG_NORMAL = "normal"
|
||||||
|
FLAG_BYPASS_ARMED_HOME = "bypass_armed_home"
|
||||||
|
|
||||||
|
BinarySensorFlags = {
|
||||||
|
FLAG_NORMAL: 1 << 0,
|
||||||
|
FLAG_BYPASS_ARMED_HOME: 1 << 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateAlarmControlPanel = template_ns.class_(
|
||||||
|
"TemplateAlarmControlPanel", alarm_control_panel.AlarmControlPanel, cg.Component
|
||||||
|
)
|
||||||
|
|
||||||
|
TemplateAlarmControlPanelRestoreMode = template_ns.enum(
|
||||||
|
"TemplateAlarmControlPanelRestoreMode"
|
||||||
|
)
|
||||||
|
RESTORE_MODES = {
|
||||||
|
"ALWAYS_DISARMED": TemplateAlarmControlPanelRestoreMode.ALARM_CONTROL_PANEL_ALWAYS_DISARMED,
|
||||||
|
"RESTORE_DEFAULT_DISARMED": TemplateAlarmControlPanelRestoreMode.ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_config(config):
|
||||||
|
if config.get(CONF_REQUIRES_CODE_TO_ARM, False) and not config.get(CONF_CODES, []):
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{CONF_REQUIRES_CODE_TO_ARM} cannot be True when there are no codes."
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
TEMPLATE_ALARM_CONTROL_PANEL_BINARY_SENSOR_SCHEMA = cv.maybe_simple_value(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_INPUT): cv.use_id(binary_sensor.BinarySensor),
|
||||||
|
cv.Optional(CONF_BYPASS_ARMED_HOME, default=False): cv.boolean,
|
||||||
|
},
|
||||||
|
key=CONF_INPUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
TEMPLATE_ALARM_CONTROL_PANEL_SCHEMA = (
|
||||||
|
alarm_control_panel.ALARM_CONTROL_PANEL_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(TemplateAlarmControlPanel),
|
||||||
|
cv.Optional(CONF_CODES): cv.ensure_list(cv.string_strict),
|
||||||
|
cv.Optional(CONF_REQUIRES_CODE_TO_ARM): cv.boolean,
|
||||||
|
cv.Optional(CONF_ARMING_HOME_TIME): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_ARMING_AWAY_TIME, default="0s"
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_PENDING_TIME, default="0s"
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_TRIGGER_TIME, default="0s"
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list(
|
||||||
|
TEMPLATE_ALARM_CONTROL_PANEL_BINARY_SENSOR_SCHEMA
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_DISARMED"): cv.enum(
|
||||||
|
RESTORE_MODES, upper=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
TEMPLATE_ALARM_CONTROL_PANEL_SCHEMA,
|
||||||
|
validate_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await alarm_control_panel.register_alarm_control_panel(var, config)
|
||||||
|
if CONF_CODES in config:
|
||||||
|
for acode in config[CONF_CODES]:
|
||||||
|
cg.add(var.add_code(acode))
|
||||||
|
if CONF_REQUIRES_CODE_TO_ARM in config:
|
||||||
|
cg.add(var.set_requires_code_to_arm(config[CONF_REQUIRES_CODE_TO_ARM]))
|
||||||
|
|
||||||
|
cg.add(var.set_arming_away_time(config[CONF_ARMING_AWAY_TIME]))
|
||||||
|
cg.add(var.set_pending_time(config[CONF_PENDING_TIME]))
|
||||||
|
cg.add(var.set_trigger_time(config[CONF_TRIGGER_TIME]))
|
||||||
|
|
||||||
|
supports_arm_home = False
|
||||||
|
if CONF_ARMING_HOME_TIME in config:
|
||||||
|
cg.add(var.set_arming_home_time(config[CONF_ARMING_HOME_TIME]))
|
||||||
|
supports_arm_home = True
|
||||||
|
|
||||||
|
for sensor in config.get(CONF_BINARY_SENSORS, []):
|
||||||
|
bs = await cg.get_variable(sensor[CONF_INPUT])
|
||||||
|
flags = BinarySensorFlags[FLAG_NORMAL]
|
||||||
|
if sensor[CONF_BYPASS_ARMED_HOME]:
|
||||||
|
flags |= BinarySensorFlags[FLAG_BYPASS_ARMED_HOME]
|
||||||
|
supports_arm_home = True
|
||||||
|
cg.add(var.add_sensor(bs, flags))
|
||||||
|
|
||||||
|
cg.add(var.set_supports_arm_home(supports_arm_home))
|
||||||
|
|
||||||
|
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
|
@ -0,0 +1,180 @@
|
||||||
|
#include "template_alarm_control_panel.h"
|
||||||
|
#include <utility>
|
||||||
|
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace template_ {
|
||||||
|
|
||||||
|
using namespace esphome::alarm_control_panel;
|
||||||
|
|
||||||
|
static const char *const TAG = "template.alarm_control_panel";
|
||||||
|
|
||||||
|
TemplateAlarmControlPanel::TemplateAlarmControlPanel(){};
|
||||||
|
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
void TemplateAlarmControlPanel::add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags) {
|
||||||
|
this->sensor_map_[sensor] = flags;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void TemplateAlarmControlPanel::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "TemplateAlarmControlPanel:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Current State: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(this->current_state_)));
|
||||||
|
ESP_LOGCONFIG(TAG, " Number of Codes: %u", this->codes_.size());
|
||||||
|
if (!this->codes_.empty())
|
||||||
|
ESP_LOGCONFIG(TAG, " Requires Code To Arm: %s", YESNO(this->requires_code_to_arm_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Arming Away Time: %us", (this->arming_away_time_ / 1000));
|
||||||
|
if (this->arming_home_time_ != 0)
|
||||||
|
ESP_LOGCONFIG(TAG, " Arming Home Time: %us", (this->arming_home_time_ / 1000));
|
||||||
|
ESP_LOGCONFIG(TAG, " Pending Time: %us", (this->pending_time_ / 1000));
|
||||||
|
ESP_LOGCONFIG(TAG, " Trigger Time: %us", (this->trigger_time_ / 1000));
|
||||||
|
ESP_LOGCONFIG(TAG, " Supported Features: %u", this->get_supported_features());
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
for (auto sensor_pair : this->sensor_map_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Binary Sesnsor:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Name: %s", sensor_pair.first->get_name().c_str());
|
||||||
|
ESP_LOGCONFIG(TAG, " Armed home bypass: %s",
|
||||||
|
TRUEFALSE(sensor_pair.second & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemplateAlarmControlPanel::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up Template AlarmControlPanel '%s'...", this->name_.c_str());
|
||||||
|
switch (this->restore_mode_) {
|
||||||
|
case ALARM_CONTROL_PANEL_ALWAYS_DISARMED:
|
||||||
|
this->current_state_ = ACP_STATE_DISARMED;
|
||||||
|
break;
|
||||||
|
case ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED: {
|
||||||
|
uint8_t value;
|
||||||
|
this->pref_ = global_preferences->make_preference<uint8_t>(this->get_object_id_hash());
|
||||||
|
if (this->pref_.load(&value)) {
|
||||||
|
this->current_state_ = static_cast<alarm_control_panel::AlarmControlPanelState>(value);
|
||||||
|
} else {
|
||||||
|
this->current_state_ = ACP_STATE_DISARMED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->desired_state_ = this->current_state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemplateAlarmControlPanel::loop() {
|
||||||
|
// change from ARMING to ARMED_x after the arming_time_ has passed
|
||||||
|
if (this->current_state_ == ACP_STATE_ARMING) {
|
||||||
|
auto delay = this->arming_away_time_;
|
||||||
|
if (this->desired_state_ == ACP_STATE_ARMED_HOME) {
|
||||||
|
delay = this->arming_home_time_;
|
||||||
|
}
|
||||||
|
if ((millis() - this->last_update_) > delay) {
|
||||||
|
this->publish_state(this->desired_state_);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// change from PENDING to TRIGGERED after the delay_time_ has passed
|
||||||
|
if (this->current_state_ == ACP_STATE_PENDING && (millis() - this->last_update_) > this->pending_time_) {
|
||||||
|
this->publish_state(ACP_STATE_TRIGGERED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto future_state = this->current_state_;
|
||||||
|
// reset triggered if all clear
|
||||||
|
if (this->current_state_ == ACP_STATE_TRIGGERED && this->trigger_time_ > 0 &&
|
||||||
|
(millis() - this->last_update_) > this->trigger_time_) {
|
||||||
|
future_state = this->desired_state_;
|
||||||
|
}
|
||||||
|
bool trigger = false;
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
if (this->is_state_armed(future_state)) {
|
||||||
|
// TODO might be better to register change for each sensor in setup...
|
||||||
|
for (auto sensor_pair : this->sensor_map_) {
|
||||||
|
if (sensor_pair.first->state) {
|
||||||
|
if (this->current_state_ == ACP_STATE_ARMED_HOME &&
|
||||||
|
(sensor_pair.second & BINARY_SENSOR_MODE_BYPASS_ARMED_HOME)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
trigger = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (trigger) {
|
||||||
|
if (this->pending_time_ > 0 && this->current_state_ != ACP_STATE_TRIGGERED) {
|
||||||
|
this->publish_state(ACP_STATE_PENDING);
|
||||||
|
} else {
|
||||||
|
this->publish_state(ACP_STATE_TRIGGERED);
|
||||||
|
}
|
||||||
|
} else if (future_state != this->current_state_) {
|
||||||
|
this->publish_state(future_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TemplateAlarmControlPanel::is_code_valid_(optional<std::string> code) {
|
||||||
|
if (!this->codes_.empty()) {
|
||||||
|
if (code.has_value()) {
|
||||||
|
ESP_LOGVV(TAG, "Checking code: %s", code.value().c_str());
|
||||||
|
return (std::count(this->codes_.begin(), this->codes_.end(), code.value()) == 1);
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "No code provided");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TemplateAlarmControlPanel::get_supported_features() const {
|
||||||
|
uint32_t features = ACP_FEAT_ARM_AWAY | ACP_FEAT_TRIGGER;
|
||||||
|
if (this->supports_arm_home_) {
|
||||||
|
features |= ACP_FEAT_ARM_HOME;
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TemplateAlarmControlPanel::get_requires_code() const { return !this->codes_.empty(); }
|
||||||
|
|
||||||
|
void TemplateAlarmControlPanel::arm_(optional<std::string> code, alarm_control_panel::AlarmControlPanelState state,
|
||||||
|
uint32_t delay) {
|
||||||
|
if (this->current_state_ != ACP_STATE_DISARMED) {
|
||||||
|
ESP_LOGW(TAG, "Cannot arm when not disarmed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->requires_code_to_arm_ && !this->is_code_valid_(std::move(code))) {
|
||||||
|
ESP_LOGW(TAG, "Not arming code doesn't match");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->desired_state_ = state;
|
||||||
|
if (delay > 0) {
|
||||||
|
this->publish_state(ACP_STATE_ARMING);
|
||||||
|
} else {
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) {
|
||||||
|
if (call.get_state()) {
|
||||||
|
if (call.get_state() == ACP_STATE_ARMED_AWAY) {
|
||||||
|
this->arm_(call.get_code(), ACP_STATE_ARMED_AWAY, this->arming_away_time_);
|
||||||
|
} else if (call.get_state() == ACP_STATE_ARMED_HOME) {
|
||||||
|
this->arm_(call.get_code(), ACP_STATE_ARMED_HOME, this->arming_home_time_);
|
||||||
|
} else if (call.get_state() == ACP_STATE_DISARMED) {
|
||||||
|
if (!this->is_code_valid_(call.get_code())) {
|
||||||
|
ESP_LOGW(TAG, "Not disarming code doesn't match");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->desired_state_ = ACP_STATE_DISARMED;
|
||||||
|
this->publish_state(ACP_STATE_DISARMED);
|
||||||
|
} else if (call.get_state() == ACP_STATE_TRIGGERED) {
|
||||||
|
this->publish_state(ACP_STATE_TRIGGERED);
|
||||||
|
} else if (call.get_state() == ACP_STATE_PENDING) {
|
||||||
|
this->publish_state(ACP_STATE_PENDING);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "State not yet implemented: %s",
|
||||||
|
LOG_STR_ARG(alarm_control_panel_state_to_string(*call.get_state())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace template_
|
||||||
|
} // namespace esphome
|
|
@ -0,0 +1,116 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
|
||||||
|
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace template_ {
|
||||||
|
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
enum BinarySensorFlags : uint16_t {
|
||||||
|
BINARY_SENSOR_MODE_NORMAL = 1 << 0,
|
||||||
|
BINARY_SENSOR_MODE_BYPASS_ARMED_HOME = 1 << 1,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum TemplateAlarmControlPanelRestoreMode {
|
||||||
|
ALARM_CONTROL_PANEL_ALWAYS_DISARMED,
|
||||||
|
ALARM_CONTROL_PANEL_RESTORE_DEFAULT_DISARMED,
|
||||||
|
};
|
||||||
|
|
||||||
|
class TemplateAlarmControlPanel : public alarm_control_panel::AlarmControlPanel, public Component {
|
||||||
|
public:
|
||||||
|
TemplateAlarmControlPanel();
|
||||||
|
void dump_config() override;
|
||||||
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
|
uint32_t get_supported_features() const override;
|
||||||
|
bool get_requires_code() const override;
|
||||||
|
bool get_requires_code_to_arm() const override { return this->requires_code_to_arm_; }
|
||||||
|
void set_restore_mode(TemplateAlarmControlPanelRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
|
||||||
|
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
/** Add a binary_sensor to the alarm_panel.
|
||||||
|
*
|
||||||
|
* @param sensor The BinarySensor instance.
|
||||||
|
* @param ignore_when_home if this should be ignored when armed_home mode
|
||||||
|
*/
|
||||||
|
void add_sensor(binary_sensor::BinarySensor *sensor, uint16_t flags = 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** add a code
|
||||||
|
*
|
||||||
|
* @param code The code
|
||||||
|
*/
|
||||||
|
void add_code(const std::string &code) { this->codes_.push_back(code); }
|
||||||
|
|
||||||
|
/** set requires a code to arm
|
||||||
|
*
|
||||||
|
* @param code_to_arm The requires code to arm
|
||||||
|
*/
|
||||||
|
void set_requires_code_to_arm(bool code_to_arm) { this->requires_code_to_arm_ = code_to_arm; }
|
||||||
|
|
||||||
|
/** set the delay before arming away
|
||||||
|
*
|
||||||
|
* @param time The milliseconds
|
||||||
|
*/
|
||||||
|
void set_arming_away_time(uint32_t time) { this->arming_away_time_ = time; }
|
||||||
|
|
||||||
|
/** set the delay before arming home
|
||||||
|
*
|
||||||
|
* @param time The milliseconds
|
||||||
|
*/
|
||||||
|
void set_arming_home_time(uint32_t time) { this->arming_home_time_ = time; }
|
||||||
|
|
||||||
|
/** set the delay before triggering
|
||||||
|
*
|
||||||
|
* @param time The milliseconds
|
||||||
|
*/
|
||||||
|
void set_pending_time(uint32_t time) { this->pending_time_ = time; }
|
||||||
|
|
||||||
|
/** set the delay before resetting after triggered
|
||||||
|
*
|
||||||
|
* @param time The milliseconds
|
||||||
|
*/
|
||||||
|
void set_trigger_time(uint32_t time) { this->trigger_time_ = time; }
|
||||||
|
|
||||||
|
void set_supports_arm_home(bool supports_arm_home) { supports_arm_home_ = supports_arm_home; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void control(const alarm_control_panel::AlarmControlPanelCall &call) override;
|
||||||
|
#ifdef USE_BINARY_SENSOR
|
||||||
|
// the map of binary sensors that the alarm_panel monitors with their modes
|
||||||
|
std::map<binary_sensor::BinarySensor *, uint16_t> sensor_map_;
|
||||||
|
#endif
|
||||||
|
TemplateAlarmControlPanelRestoreMode restore_mode_{};
|
||||||
|
|
||||||
|
// the arming away delay
|
||||||
|
uint32_t arming_away_time_;
|
||||||
|
// the arming home delay
|
||||||
|
uint32_t arming_home_time_{0};
|
||||||
|
// the trigger delay
|
||||||
|
uint32_t pending_time_;
|
||||||
|
// the time in trigger
|
||||||
|
uint32_t trigger_time_;
|
||||||
|
// a list of codes
|
||||||
|
std::vector<std::string> codes_;
|
||||||
|
// requires a code to arm
|
||||||
|
bool requires_code_to_arm_ = false;
|
||||||
|
bool supports_arm_home_ = false;
|
||||||
|
// check if the code is valid
|
||||||
|
bool is_code_valid_(optional<std::string> code);
|
||||||
|
|
||||||
|
void arm_(optional<std::string> code, alarm_control_panel::AlarmControlPanelState state, uint32_t delay);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace template_
|
||||||
|
} // namespace esphome
|
|
@ -15,6 +15,7 @@ VBus = vbus_ns.class_("VBus", uart.UARTDevice, cg.Component)
|
||||||
CONF_VBUS_ID = "vbus_id"
|
CONF_VBUS_ID = "vbus_id"
|
||||||
|
|
||||||
CONF_DELTASOL_BS_PLUS = "deltasol_bs_plus"
|
CONF_DELTASOL_BS_PLUS = "deltasol_bs_plus"
|
||||||
|
CONF_DELTASOL_BS_2009 = "deltasol_bs_2009"
|
||||||
CONF_DELTASOL_C = "deltasol_c"
|
CONF_DELTASOL_C = "deltasol_c"
|
||||||
CONF_DELTASOL_CS2 = "deltasol_cs2"
|
CONF_DELTASOL_CS2 = "deltasol_cs2"
|
||||||
CONF_DELTASOL_CS_PLUS = "deltasol_cs_plus"
|
CONF_DELTASOL_CS_PLUS = "deltasol_cs_plus"
|
||||||
|
|
|
@ -18,12 +18,14 @@ from .. import (
|
||||||
VBus,
|
VBus,
|
||||||
CONF_VBUS_ID,
|
CONF_VBUS_ID,
|
||||||
CONF_DELTASOL_BS_PLUS,
|
CONF_DELTASOL_BS_PLUS,
|
||||||
|
CONF_DELTASOL_BS_2009,
|
||||||
CONF_DELTASOL_C,
|
CONF_DELTASOL_C,
|
||||||
CONF_DELTASOL_CS2,
|
CONF_DELTASOL_CS2,
|
||||||
CONF_DELTASOL_CS_PLUS,
|
CONF_DELTASOL_CS_PLUS,
|
||||||
)
|
)
|
||||||
|
|
||||||
DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusBSensor", cg.Component)
|
DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusBSensor", cg.Component)
|
||||||
|
DeltaSol_BS_2009 = vbus_ns.class_("DeltaSolBS2009BSensor", cg.Component)
|
||||||
DeltaSol_C = vbus_ns.class_("DeltaSolCBSensor", cg.Component)
|
DeltaSol_C = vbus_ns.class_("DeltaSolCBSensor", cg.Component)
|
||||||
DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2BSensor", cg.Component)
|
DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2BSensor", cg.Component)
|
||||||
DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusBSensor", cg.Component)
|
DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusBSensor", cg.Component)
|
||||||
|
@ -42,6 +44,7 @@ CONF_COLLECTOR_FROST = "collector_frost"
|
||||||
CONF_TUBE_COLLECTOR = "tube_collector"
|
CONF_TUBE_COLLECTOR = "tube_collector"
|
||||||
CONF_RECOOLING = "recooling"
|
CONF_RECOOLING = "recooling"
|
||||||
CONF_HQM = "hqm"
|
CONF_HQM = "hqm"
|
||||||
|
CONF_FROST_PROTECTION_ACTIVE = "frost_protection_active"
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.typed_schema(
|
CONFIG_SCHEMA = cv.typed_schema(
|
||||||
{
|
{
|
||||||
|
@ -87,6 +90,33 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
CONF_DELTASOL_BS_2009: cv.COMPONENT_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(DeltaSol_BS_2009),
|
||||||
|
cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
|
||||||
|
cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema(
|
||||||
|
device_class=DEVICE_CLASS_PROBLEM,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema(
|
||||||
|
device_class=DEVICE_CLASS_PROBLEM,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema(
|
||||||
|
device_class=DEVICE_CLASS_PROBLEM,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema(
|
||||||
|
device_class=DEVICE_CLASS_PROBLEM,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
cv.Optional(
|
||||||
|
CONF_FROST_PROTECTION_ACTIVE
|
||||||
|
): binary_sensor.binary_sensor_schema(
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
|
CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(DeltaSol_C),
|
cv.GenerateID(): cv.declare_id(DeltaSol_C),
|
||||||
|
@ -222,6 +252,28 @@ async def to_code(config):
|
||||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HQM])
|
sens = await binary_sensor.new_binary_sensor(config[CONF_HQM])
|
||||||
cg.add(var.set_hqm_bsensor(sens))
|
cg.add(var.set_hqm_bsensor(sens))
|
||||||
|
|
||||||
|
elif config[CONF_MODEL] == CONF_DELTASOL_BS_2009:
|
||||||
|
cg.add(var.set_command(0x0100))
|
||||||
|
cg.add(var.set_source(0x427B))
|
||||||
|
cg.add(var.set_dest(0x0010))
|
||||||
|
if CONF_SENSOR1_ERROR in config:
|
||||||
|
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR])
|
||||||
|
cg.add(var.set_s1_error_bsensor(sens))
|
||||||
|
if CONF_SENSOR2_ERROR in config:
|
||||||
|
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR])
|
||||||
|
cg.add(var.set_s2_error_bsensor(sens))
|
||||||
|
if CONF_SENSOR3_ERROR in config:
|
||||||
|
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR])
|
||||||
|
cg.add(var.set_s3_error_bsensor(sens))
|
||||||
|
if CONF_SENSOR4_ERROR in config:
|
||||||
|
sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR])
|
||||||
|
cg.add(var.set_s4_error_bsensor(sens))
|
||||||
|
if CONF_FROST_PROTECTION_ACTIVE in config:
|
||||||
|
sens = await binary_sensor.new_binary_sensor(
|
||||||
|
config[CONF_FROST_PROTECTION_ACTIVE]
|
||||||
|
)
|
||||||
|
cg.add(var.set_frost_protection_active_bsensor(sens))
|
||||||
|
|
||||||
elif config[CONF_MODEL] == CONF_DELTASOL_C:
|
elif config[CONF_MODEL] == CONF_DELTASOL_C:
|
||||||
cg.add(var.set_command(0x0100))
|
cg.add(var.set_command(0x0100))
|
||||||
cg.add(var.set_source(0x4212))
|
cg.add(var.set_source(0x4212))
|
||||||
|
|
|
@ -50,6 +50,28 @@ void DeltaSolBSPlusBSensor::handle_message(std::vector<uint8_t> &message) {
|
||||||
this->hqm_bsensor_->publish_state(message[15] & 0x20);
|
this->hqm_bsensor_->publish_state(message[15] & 0x20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeltaSolBS2009BSensor::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Deltasol BS 2009:");
|
||||||
|
LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_);
|
||||||
|
LOG_BINARY_SENSOR(" ", "Sensor 2 Error", this->s2_error_bsensor_);
|
||||||
|
LOG_BINARY_SENSOR(" ", "Sensor 3 Error", this->s3_error_bsensor_);
|
||||||
|
LOG_BINARY_SENSOR(" ", "Sensor 4 Error", this->s4_error_bsensor_);
|
||||||
|
LOG_BINARY_SENSOR(" ", "Frost Protection Active", this->frost_protection_active_bsensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeltaSolBS2009BSensor::handle_message(std::vector<uint8_t> &message) {
|
||||||
|
if (this->s1_error_bsensor_ != nullptr)
|
||||||
|
this->s1_error_bsensor_->publish_state(message[20] & 1);
|
||||||
|
if (this->s2_error_bsensor_ != nullptr)
|
||||||
|
this->s2_error_bsensor_->publish_state(message[20] & 2);
|
||||||
|
if (this->s3_error_bsensor_ != nullptr)
|
||||||
|
this->s3_error_bsensor_->publish_state(message[20] & 4);
|
||||||
|
if (this->s4_error_bsensor_ != nullptr)
|
||||||
|
this->s4_error_bsensor_->publish_state(message[20] & 8);
|
||||||
|
if (this->frost_protection_active_bsensor_ != nullptr)
|
||||||
|
this->frost_protection_active_bsensor_->publish_state(message[25] & 1);
|
||||||
|
}
|
||||||
|
|
||||||
void DeltaSolCBSensor::dump_config() {
|
void DeltaSolCBSensor::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Deltasol C:");
|
ESP_LOGCONFIG(TAG, "Deltasol C:");
|
||||||
LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_);
|
LOG_BINARY_SENSOR(" ", "Sensor 1 Error", this->s1_error_bsensor_);
|
||||||
|
|
|
@ -39,6 +39,27 @@ class DeltaSolBSPlusBSensor : public VBusListener, public Component {
|
||||||
void handle_message(std::vector<uint8_t> &message) override;
|
void handle_message(std::vector<uint8_t> &message) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DeltaSolBS2009BSensor : public VBusListener, public Component {
|
||||||
|
public:
|
||||||
|
void dump_config() override;
|
||||||
|
void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; }
|
||||||
|
void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; }
|
||||||
|
void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; }
|
||||||
|
void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; }
|
||||||
|
void set_frost_protection_active_bsensor(binary_sensor::BinarySensor *bsensor) {
|
||||||
|
this->frost_protection_active_bsensor_ = bsensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
binary_sensor::BinarySensor *s1_error_bsensor_{nullptr};
|
||||||
|
binary_sensor::BinarySensor *s2_error_bsensor_{nullptr};
|
||||||
|
binary_sensor::BinarySensor *s3_error_bsensor_{nullptr};
|
||||||
|
binary_sensor::BinarySensor *s4_error_bsensor_{nullptr};
|
||||||
|
binary_sensor::BinarySensor *frost_protection_active_bsensor_{nullptr};
|
||||||
|
|
||||||
|
void handle_message(std::vector<uint8_t> &message) override;
|
||||||
|
};
|
||||||
|
|
||||||
class DeltaSolCBSensor : public VBusListener, public Component {
|
class DeltaSolCBSensor : public VBusListener, public Component {
|
||||||
public:
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
|
@ -33,12 +33,14 @@ from .. import (
|
||||||
VBus,
|
VBus,
|
||||||
CONF_VBUS_ID,
|
CONF_VBUS_ID,
|
||||||
CONF_DELTASOL_BS_PLUS,
|
CONF_DELTASOL_BS_PLUS,
|
||||||
|
CONF_DELTASOL_BS_2009,
|
||||||
CONF_DELTASOL_C,
|
CONF_DELTASOL_C,
|
||||||
CONF_DELTASOL_CS2,
|
CONF_DELTASOL_CS2,
|
||||||
CONF_DELTASOL_CS_PLUS,
|
CONF_DELTASOL_CS_PLUS,
|
||||||
)
|
)
|
||||||
|
|
||||||
DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusSensor", cg.Component)
|
DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusSensor", cg.Component)
|
||||||
|
DeltaSol_BS_2009 = vbus_ns.class_("DeltaSolBS2009Sensor", cg.Component)
|
||||||
DeltaSol_C = vbus_ns.class_("DeltaSolCSensor", cg.Component)
|
DeltaSol_C = vbus_ns.class_("DeltaSolCSensor", cg.Component)
|
||||||
DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2Sensor", cg.Component)
|
DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2Sensor", cg.Component)
|
||||||
DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusSensor", cg.Component)
|
DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusSensor", cg.Component)
|
||||||
|
@ -142,6 +144,87 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
CONF_DELTASOL_BS_2009: cv.COMPONENT_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(DeltaSol_BS_2009),
|
||||||
|
cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
|
||||||
|
cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_THERMOMETER,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_THERMOMETER,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_THERMOMETER,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_THERMOMETER,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
icon=ICON_PERCENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_EMPTY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
icon=ICON_PERCENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_EMPTY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_HOUR,
|
||||||
|
icon=ICON_TIMER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_DURATION,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_HOUR,
|
||||||
|
icon=ICON_TIMER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_DURATION,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT_HOURS,
|
||||||
|
icon=ICON_RADIATOR,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TIME): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_MINUTE,
|
||||||
|
icon=ICON_TIMER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_DURATION,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_VERSION): sensor.sensor_schema(
|
||||||
|
accuracy_decimals=2,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
|
CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(DeltaSol_C),
|
cv.GenerateID(): cv.declare_id(DeltaSol_C),
|
||||||
|
@ -437,6 +520,44 @@ async def to_code(config):
|
||||||
sens = await sensor.new_sensor(config[CONF_VERSION])
|
sens = await sensor.new_sensor(config[CONF_VERSION])
|
||||||
cg.add(var.set_version_sensor(sens))
|
cg.add(var.set_version_sensor(sens))
|
||||||
|
|
||||||
|
elif config[CONF_MODEL] == CONF_DELTASOL_BS_2009:
|
||||||
|
cg.add(var.set_command(0x0100))
|
||||||
|
cg.add(var.set_source(0x427B))
|
||||||
|
cg.add(var.set_dest(0x0010))
|
||||||
|
if CONF_TEMPERATURE_1 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1])
|
||||||
|
cg.add(var.set_temperature1_sensor(sens))
|
||||||
|
if CONF_TEMPERATURE_2 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2])
|
||||||
|
cg.add(var.set_temperature2_sensor(sens))
|
||||||
|
if CONF_TEMPERATURE_3 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3])
|
||||||
|
cg.add(var.set_temperature3_sensor(sens))
|
||||||
|
if CONF_TEMPERATURE_4 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4])
|
||||||
|
cg.add(var.set_temperature4_sensor(sens))
|
||||||
|
if CONF_PUMP_SPEED_1 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1])
|
||||||
|
cg.add(var.set_pump_speed1_sensor(sens))
|
||||||
|
if CONF_PUMP_SPEED_2 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2])
|
||||||
|
cg.add(var.set_pump_speed2_sensor(sens))
|
||||||
|
if CONF_OPERATING_HOURS_1 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1])
|
||||||
|
cg.add(var.set_operating_hours1_sensor(sens))
|
||||||
|
if CONF_OPERATING_HOURS_2 in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2])
|
||||||
|
cg.add(var.set_operating_hours2_sensor(sens))
|
||||||
|
if CONF_HEAT_QUANTITY in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY])
|
||||||
|
cg.add(var.set_heat_quantity_sensor(sens))
|
||||||
|
if CONF_TIME in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TIME])
|
||||||
|
cg.add(var.set_time_sensor(sens))
|
||||||
|
if CONF_VERSION in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_VERSION])
|
||||||
|
cg.add(var.set_version_sensor(sens))
|
||||||
|
|
||||||
elif config[CONF_MODEL] == CONF_DELTASOL_C:
|
elif config[CONF_MODEL] == CONF_DELTASOL_C:
|
||||||
cg.add(var.set_command(0x0100))
|
cg.add(var.set_command(0x0100))
|
||||||
cg.add(var.set_source(0x4212))
|
cg.add(var.set_source(0x4212))
|
||||||
|
|
|
@ -57,6 +57,47 @@ void DeltaSolBSPlusSensor::handle_message(std::vector<uint8_t> &message) {
|
||||||
this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f);
|
this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeltaSolBS2009Sensor::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Deltasol BS 2009:");
|
||||||
|
LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature 2", this->temperature2_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature 3", this->temperature3_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature 4", this->temperature4_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Pump Speed 1", this->pump_speed1_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Pump Speed 2", this->pump_speed2_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Operating Hours 1", this->operating_hours1_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Operating Hours 2", this->operating_hours2_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Heat Quantity", this->heat_quantity_sensor_);
|
||||||
|
LOG_SENSOR(" ", "System Time", this->time_sensor_);
|
||||||
|
LOG_SENSOR(" ", "FW Version", this->version_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeltaSolBS2009Sensor::handle_message(std::vector<uint8_t> &message) {
|
||||||
|
if (this->temperature1_sensor_ != nullptr)
|
||||||
|
this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f);
|
||||||
|
if (this->temperature2_sensor_ != nullptr)
|
||||||
|
this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f);
|
||||||
|
if (this->temperature3_sensor_ != nullptr)
|
||||||
|
this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f);
|
||||||
|
if (this->temperature4_sensor_ != nullptr)
|
||||||
|
this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f);
|
||||||
|
if (this->pump_speed1_sensor_ != nullptr)
|
||||||
|
this->pump_speed1_sensor_->publish_state(message[8]);
|
||||||
|
if (this->pump_speed2_sensor_ != nullptr)
|
||||||
|
this->pump_speed2_sensor_->publish_state(message[12]);
|
||||||
|
if (this->operating_hours1_sensor_ != nullptr)
|
||||||
|
this->operating_hours1_sensor_->publish_state(get_u16(message, 10));
|
||||||
|
if (this->operating_hours2_sensor_ != nullptr)
|
||||||
|
this->operating_hours2_sensor_->publish_state(get_u16(message, 18));
|
||||||
|
if (this->heat_quantity_sensor_ != nullptr) {
|
||||||
|
this->heat_quantity_sensor_->publish_state(get_u16(message, 28) + get_u16(message, 30) * 1000);
|
||||||
|
}
|
||||||
|
if (this->time_sensor_ != nullptr)
|
||||||
|
this->time_sensor_->publish_state(get_u16(message, 22));
|
||||||
|
if (this->version_sensor_ != nullptr)
|
||||||
|
this->version_sensor_->publish_state(get_u16(message, 32) * 0.01f);
|
||||||
|
}
|
||||||
|
|
||||||
void DeltaSolCSensor::dump_config() {
|
void DeltaSolCSensor::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Deltasol C:");
|
ESP_LOGCONFIG(TAG, "Deltasol C:");
|
||||||
LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_);
|
LOG_SENSOR(" ", "Temperature 1", this->temperature1_sensor_);
|
||||||
|
@ -166,9 +207,9 @@ void DeltaSolCSPlusSensor::handle_message(std::vector<uint8_t> &message) {
|
||||||
if (this->heat_quantity_sensor_ != nullptr)
|
if (this->heat_quantity_sensor_ != nullptr)
|
||||||
this->heat_quantity_sensor_->publish_state((get_u16(message, 30) << 16) + get_u16(message, 28));
|
this->heat_quantity_sensor_->publish_state((get_u16(message, 30) << 16) + get_u16(message, 28));
|
||||||
if (this->time_sensor_ != nullptr)
|
if (this->time_sensor_ != nullptr)
|
||||||
this->time_sensor_->publish_state(get_u16(message, 12));
|
this->time_sensor_->publish_state(get_u16(message, 22));
|
||||||
if (this->version_sensor_ != nullptr)
|
if (this->version_sensor_ != nullptr)
|
||||||
this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f);
|
this->version_sensor_->publish_state(get_u16(message, 32) * 0.01f);
|
||||||
if (this->flow_rate_sensor_ != nullptr)
|
if (this->flow_rate_sensor_ != nullptr)
|
||||||
this->flow_rate_sensor_->publish_state(get_u16(message, 38));
|
this->flow_rate_sensor_->publish_state(get_u16(message, 38));
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,37 @@ class DeltaSolBSPlusSensor : public VBusListener, public Component {
|
||||||
void handle_message(std::vector<uint8_t> &message) override;
|
void handle_message(std::vector<uint8_t> &message) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DeltaSolBS2009Sensor : public VBusListener, public Component {
|
||||||
|
public:
|
||||||
|
void dump_config() override;
|
||||||
|
void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; }
|
||||||
|
void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; }
|
||||||
|
void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; }
|
||||||
|
void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; }
|
||||||
|
void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; }
|
||||||
|
void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; }
|
||||||
|
void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; }
|
||||||
|
void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; }
|
||||||
|
void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; }
|
||||||
|
void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; }
|
||||||
|
void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *temperature1_sensor_{nullptr};
|
||||||
|
sensor::Sensor *temperature2_sensor_{nullptr};
|
||||||
|
sensor::Sensor *temperature3_sensor_{nullptr};
|
||||||
|
sensor::Sensor *temperature4_sensor_{nullptr};
|
||||||
|
sensor::Sensor *pump_speed1_sensor_{nullptr};
|
||||||
|
sensor::Sensor *pump_speed2_sensor_{nullptr};
|
||||||
|
sensor::Sensor *operating_hours1_sensor_{nullptr};
|
||||||
|
sensor::Sensor *operating_hours2_sensor_{nullptr};
|
||||||
|
sensor::Sensor *heat_quantity_sensor_{nullptr};
|
||||||
|
sensor::Sensor *time_sensor_{nullptr};
|
||||||
|
sensor::Sensor *version_sensor_{nullptr};
|
||||||
|
|
||||||
|
void handle_message(std::vector<uint8_t> &message) override;
|
||||||
|
};
|
||||||
|
|
||||||
class DeltaSolCSensor : public VBusListener, public Component {
|
class DeltaSolCSensor : public VBusListener, public Component {
|
||||||
public:
|
public:
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
|
@ -91,6 +91,16 @@ bool ListEntitiesIterator::on_select(select::Select *select) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
|
this->web_server_->events_.send(
|
||||||
|
this->web_server_->alarm_control_panel_json(a_alarm_control_panel, a_alarm_control_panel->get_state(), DETAIL_ALL)
|
||||||
|
.c_str(),
|
||||||
|
"state");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace web_server
|
} // namespace web_server
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,9 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool on_lock(lock::Lock *a_lock) override;
|
bool on_lock(lock::Lock *a_lock) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WebServer *web_server_;
|
WebServer *web_server_;
|
||||||
|
|
|
@ -813,6 +813,9 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Longest: HORIZONTAL
|
||||||
|
#define PSTR_LOCAL(mode_s) strncpy_P(buf, (PGM_P) ((mode_s)), 15)
|
||||||
|
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
void WebServer::on_climate_update(climate::Climate *obj) {
|
void WebServer::on_climate_update(climate::Climate *obj) {
|
||||||
this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
|
this->events_.send(this->climate_json(obj, DETAIL_STATE).c_str(), "state");
|
||||||
|
@ -869,16 +872,13 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Longest: HORIZONTAL
|
|
||||||
#define PSTR_LOCAL(mode_s) strncpy_P(__buf, (PGM_P) ((mode_s)), 15)
|
|
||||||
|
|
||||||
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([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();
|
||||||
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
|
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
|
||||||
char __buf[16];
|
char buf[16];
|
||||||
|
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray opt = root.createNestedArray("modes");
|
JsonArray opt = root.createNestedArray("modes");
|
||||||
|
@ -996,6 +996,34 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
||||||
|
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) {
|
||||||
|
for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) {
|
||||||
|
if (obj->get_object_id() != match.id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (request->method() == HTTP_GET) {
|
||||||
|
std::string data = this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE);
|
||||||
|
request->send(200, "application/json", data.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request->send(404);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||||
if (request->url() == "/")
|
if (request->url() == "/")
|
||||||
return true;
|
return true;
|
||||||
|
@ -1073,6 +1101,11 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
if (request->method() == HTTP_GET && match.domain == "alarm_control_panel")
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||||
|
@ -1180,6 +1213,14 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
if (match.domain == "alarm_control_panel") {
|
||||||
|
this->handle_alarm_control_panel_request(request, match);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebServer::isRequestHandlerTrivial() { return false; }
|
bool WebServer::isRequestHandlerTrivial() { return false; }
|
||||||
|
|
|
@ -216,6 +216,17 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||||
std::string lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config);
|
std::string lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
|
||||||
|
|
||||||
|
/// Handle a alarm_control_panel request under '/alarm_control_panel/<id>'.
|
||||||
|
void handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||||
|
|
||||||
|
/// Dump the alarm_control_panel state with its value as a JSON string.
|
||||||
|
std::string alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
|
||||||
|
alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config);
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Override the web handler's canHandle method.
|
/// Override the web handler's canHandle method.
|
||||||
bool canHandle(AsyncWebServerRequest *request) override;
|
bool canHandle(AsyncWebServerRequest *request) override;
|
||||||
/// Override the web handler's handleRequest method.
|
/// Override the web handler's handleRequest method.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2023.6.0-dev"
|
__version__ = "2023.7.0-dev"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
#include "esphome/components/media_player/media_player.h"
|
#include "esphome/components/media_player/media_player.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
@ -126,6 +129,12 @@ class Application {
|
||||||
void register_media_player(media_player::MediaPlayer *media_player) { this->media_players_.push_back(media_player); }
|
void register_media_player(media_player::MediaPlayer *media_player) { this->media_players_.push_back(media_player); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
void register_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
|
this->alarm_control_panels_.push_back(a_alarm_control_panel);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Register the component in this Application instance.
|
/// Register the component in this Application instance.
|
||||||
template<class C> C *register_component(C *c) {
|
template<class C> C *register_component(C *c) {
|
||||||
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
|
static_assert(std::is_base_of<Component, C>::value, "Only Component subclasses can be registered");
|
||||||
|
@ -296,6 +305,18 @@ class Application {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
const std::vector<alarm_control_panel::AlarmControlPanel *> &get_alarm_control_panels() {
|
||||||
|
return this->alarm_control_panels_;
|
||||||
|
}
|
||||||
|
alarm_control_panel::AlarmControlPanel *get_alarm_control_panel_by_key(uint32_t key, bool include_internal = false) {
|
||||||
|
for (auto *obj : this->alarm_control_panels_)
|
||||||
|
if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal()))
|
||||||
|
return obj;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Scheduler scheduler;
|
Scheduler scheduler;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -349,6 +370,9 @@ class Application {
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
std::vector<media_player::MediaPlayer *> media_players_{};
|
std::vector<media_player::MediaPlayer *> media_players_{};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
std::vector<alarm_control_panel::AlarmControlPanel *> alarm_control_panels_{};
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
std::string friendly_name_;
|
std::string friendly_name_;
|
||||||
|
|
|
@ -246,6 +246,21 @@ void ComponentIterator::advance() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
case IteratorState::ALARM_CONTROL_PANEL:
|
||||||
|
if (this->at_ >= App.get_alarm_control_panels().size()) {
|
||||||
|
advance_platform = true;
|
||||||
|
} else {
|
||||||
|
auto *a_alarm_control_panel = App.get_alarm_control_panels()[this->at_];
|
||||||
|
if (a_alarm_control_panel->is_internal() && !this->include_internal_) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
success = this->on_alarm_control_panel(a_alarm_control_panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
case IteratorState::MAX:
|
case IteratorState::MAX:
|
||||||
if (this->on_end()) {
|
if (this->on_end()) {
|
||||||
|
|
|
@ -65,6 +65,9 @@ class ComponentIterator {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
virtual bool on_media_player(media_player::MediaPlayer *media_player);
|
virtual bool on_media_player(media_player::MediaPlayer *media_player);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
virtual bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) = 0;
|
||||||
#endif
|
#endif
|
||||||
virtual bool on_end();
|
virtual bool on_end();
|
||||||
|
|
||||||
|
@ -116,6 +119,9 @@ class ComponentIterator {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
MEDIA_PLAYER,
|
MEDIA_PLAYER,
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
ALARM_CONTROL_PANEL,
|
||||||
#endif
|
#endif
|
||||||
MAX,
|
MAX,
|
||||||
} state_{IteratorState::NONE};
|
} state_{IteratorState::NONE};
|
||||||
|
|
|
@ -79,6 +79,12 @@ void Controller::setup_controller(bool include_internal) {
|
||||||
obj->add_on_state_callback([this, obj]() { this->on_media_player_update(obj); });
|
obj->add_on_state_callback([this, obj]() { this->on_media_player_update(obj); });
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
for (auto *obj : App.get_alarm_control_panels()) {
|
||||||
|
if (include_internal || !obj->is_internal())
|
||||||
|
obj->add_on_state_callback([this, obj]() { this->on_alarm_control_panel_update(obj); });
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
#include "esphome/components/media_player/media_player.h"
|
#include "esphome/components/media_player/media_player.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
|
@ -82,6 +85,9 @@ class Controller {
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
virtual void on_media_player_update(media_player::MediaPlayer *obj){};
|
virtual void on_media_player_update(media_player::MediaPlayer *obj){};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
virtual void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj){};
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#define USE_API
|
#define USE_API
|
||||||
#define USE_API_NOISE
|
#define USE_API_NOISE
|
||||||
#define USE_API_PLAINTEXT
|
#define USE_API_PLAINTEXT
|
||||||
|
#define USE_ALARM_CONTROL_PANEL
|
||||||
#define USE_BINARY_SENSOR
|
#define USE_BINARY_SENSOR
|
||||||
#define USE_BUTTON
|
#define USE_BUTTON
|
||||||
#define USE_CLIMATE
|
#define USE_CLIMATE
|
||||||
|
|
|
@ -11,3 +11,5 @@ pytest-mock==3.10.0
|
||||||
pytest-asyncio==0.21.0
|
pytest-asyncio==0.21.0
|
||||||
asyncmock==0.4.2
|
asyncmock==0.4.2
|
||||||
hypothesis==5.49.0
|
hypothesis==5.49.0
|
||||||
|
|
||||||
|
clang-format==13.0.1 ; platform_machine != 'armv7l'
|
||||||
|
|
|
@ -18,6 +18,7 @@ will be generated, they still need to be formatted
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
|
@ -944,3 +945,19 @@ with open(root / "api_pb2_service.cpp", "w") as f:
|
||||||
f.write(cpp)
|
f.write(cpp)
|
||||||
|
|
||||||
prot.unlink()
|
prot.unlink()
|
||||||
|
|
||||||
|
try:
|
||||||
|
import clang_format
|
||||||
|
|
||||||
|
def exec_clang_format(path):
|
||||||
|
clang_format_path = os.path.join(
|
||||||
|
os.path.dirname(clang_format.__file__), "data", "bin", "clang-format"
|
||||||
|
)
|
||||||
|
call([clang_format_path, "-i", path])
|
||||||
|
|
||||||
|
exec_clang_format(root / "api_pb2_service.h")
|
||||||
|
exec_clang_format(root / "api_pb2_service.cpp")
|
||||||
|
exec_clang_format(root / "api_pb2.h")
|
||||||
|
exec_clang_format(root / "api_pb2.cpp")
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
|
@ -3417,3 +3417,21 @@ lcd_menu:
|
||||||
on_prev:
|
on_prev:
|
||||||
then:
|
then:
|
||||||
lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());'
|
lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());'
|
||||||
|
|
||||||
|
alarm_control_panel:
|
||||||
|
- platform: template
|
||||||
|
id: alarmcontrolpanel1
|
||||||
|
name: Alarm Panel
|
||||||
|
codes:
|
||||||
|
- "1234"
|
||||||
|
requires_code_to_arm: true
|
||||||
|
arming_home_time: 1s
|
||||||
|
arming_away_time: 15s
|
||||||
|
pending_time: 15s
|
||||||
|
trigger_time: 30s
|
||||||
|
binary_sensors:
|
||||||
|
- binary_sensor1
|
||||||
|
on_state:
|
||||||
|
then:
|
||||||
|
- lambda: !lambda |-
|
||||||
|
ESP_LOGD("TEST", "State change %s", alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()));
|
||||||
|
|
|
@ -822,7 +822,6 @@ switch:
|
||||||
name: R0 Switch
|
name: R0 Switch
|
||||||
component_name: page0.r0
|
component_name: page0.r0
|
||||||
|
|
||||||
|
|
||||||
climate:
|
climate:
|
||||||
- platform: bang_bang
|
- platform: bang_bang
|
||||||
name: Bang Bang Climate
|
name: Bang Bang Climate
|
||||||
|
@ -1106,7 +1105,6 @@ rf_bridge:
|
||||||
- rf_bridge.send_raw:
|
- rf_bridge.send_raw:
|
||||||
raw: "AAA5070008001000ABC12355"
|
raw: "AAA5070008001000ABC12355"
|
||||||
|
|
||||||
|
|
||||||
display:
|
display:
|
||||||
- platform: nextion
|
- platform: nextion
|
||||||
uart_id: uart_1
|
uart_id: uart_1
|
||||||
|
@ -1171,3 +1169,22 @@ daly_bms:
|
||||||
qr_code:
|
qr_code:
|
||||||
- id: homepage_qr
|
- id: homepage_qr
|
||||||
value: https://esphome.io/index.html
|
value: https://esphome.io/index.html
|
||||||
|
|
||||||
|
alarm_control_panel:
|
||||||
|
- platform: template
|
||||||
|
id: alarmcontrolpanel1
|
||||||
|
name: Alarm Panel
|
||||||
|
codes:
|
||||||
|
- "1234"
|
||||||
|
requires_code_to_arm: true
|
||||||
|
arming_home_time: 1s
|
||||||
|
arming_away_time: 15s
|
||||||
|
pending_time: 15s
|
||||||
|
trigger_time: 30s
|
||||||
|
binary_sensors:
|
||||||
|
- input: bin1
|
||||||
|
bypass_armed_home: true
|
||||||
|
on_state:
|
||||||
|
then:
|
||||||
|
- lambda: !lambda |-
|
||||||
|
ESP_LOGD("TEST", "State change %s", alarm_control_panel_state_to_string(id(alarmcontrolpanel1)->get_state()));
|
||||||
|
|
Loading…
Reference in a new issue