mirror of
https://github.com/esphome/esphome.git
synced 2025-01-09 06:11:44 +01:00
Merge branch 'dev' into msa3xx
This commit is contained in:
commit
8f060f1829
39 changed files with 814 additions and 287 deletions
|
@ -152,6 +152,10 @@ esphome/components/grove_tb6612fng/* @max246
|
||||||
esphome/components/growatt_solar/* @leeuwte
|
esphome/components/growatt_solar/* @leeuwte
|
||||||
esphome/components/gt911/* @clydebarrow @jesserockz
|
esphome/components/gt911/* @clydebarrow @jesserockz
|
||||||
esphome/components/haier/* @paveldn
|
esphome/components/haier/* @paveldn
|
||||||
|
esphome/components/haier/binary_sensor/* @paveldn
|
||||||
|
esphome/components/haier/button/* @paveldn
|
||||||
|
esphome/components/haier/sensor/* @paveldn
|
||||||
|
esphome/components/haier/text_sensor/* @paveldn
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
esphome/components/hbridge/light/* @DotNetDann
|
esphome/components/hbridge/light/* @DotNetDann
|
||||||
|
|
|
@ -46,7 +46,7 @@ template<typename... Ts> class BeeperOffAction : public Action<Ts...> {
|
||||||
template<typename... Ts> class VerticalAirflowAction : public Action<Ts...> {
|
template<typename... Ts> class VerticalAirflowAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
VerticalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
VerticalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
||||||
TEMPLATABLE_VALUE(AirflowVerticalDirection, direction)
|
TEMPLATABLE_VALUE(hon_protocol::VerticalSwingMode, direction)
|
||||||
void play(Ts... x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); }
|
void play(Ts... x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -56,7 +56,7 @@ template<typename... Ts> class VerticalAirflowAction : public Action<Ts...> {
|
||||||
template<typename... Ts> class HorizontalAirflowAction : public Action<Ts...> {
|
template<typename... Ts> class HorizontalAirflowAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
HorizontalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
HorizontalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
||||||
TEMPLATABLE_VALUE(AirflowHorizontalDirection, direction)
|
TEMPLATABLE_VALUE(hon_protocol::HorizontalSwingMode, direction)
|
||||||
void play(Ts... x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); }
|
void play(Ts... x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -11,6 +11,7 @@ from ..climate import (
|
||||||
HonClimate,
|
HonClimate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@paveldn"]
|
||||||
BinarySensorTypeEnum = HonClimate.enum("SubBinarySensorType", True)
|
BinarySensorTypeEnum = HonClimate.enum("SubBinarySensorType", True)
|
||||||
|
|
||||||
# Haier sensors
|
# Haier sensors
|
||||||
|
|
41
esphome/components/haier/button/__init__.py
Normal file
41
esphome/components/haier/button/__init__.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import button
|
||||||
|
from ..climate import (
|
||||||
|
CONF_HAIER_ID,
|
||||||
|
HonClimate,
|
||||||
|
haier_ns,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@paveldn"]
|
||||||
|
SelfCleaningButton = haier_ns.class_("SelfCleaningButton", button.Button)
|
||||||
|
SteriCleaningButton = haier_ns.class_("SteriCleaningButton", button.Button)
|
||||||
|
|
||||||
|
|
||||||
|
# Haier buttons
|
||||||
|
CONF_SELF_CLEANING = "self_cleaning"
|
||||||
|
CONF_STERI_CLEANING = "steri_cleaning"
|
||||||
|
|
||||||
|
# Additional icons
|
||||||
|
ICON_SPRAY_BOTTLE = "mdi:spray-bottle"
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_HAIER_ID): cv.use_id(HonClimate),
|
||||||
|
cv.Optional(CONF_SELF_CLEANING): button.button_schema(
|
||||||
|
SelfCleaningButton,
|
||||||
|
icon=ICON_SPRAY_BOTTLE,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_STERI_CLEANING): button.button_schema(
|
||||||
|
SteriCleaningButton,
|
||||||
|
icon=ICON_SPRAY_BOTTLE,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
for button_type in [CONF_SELF_CLEANING, CONF_STERI_CLEANING]:
|
||||||
|
if conf := config.get(button_type):
|
||||||
|
btn = await button.new_button(conf)
|
||||||
|
await cg.register_parented(btn, config[CONF_HAIER_ID])
|
9
esphome/components/haier/button/self_cleaning.cpp
Normal file
9
esphome/components/haier/button/self_cleaning.cpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#include "self_cleaning.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void SelfCleaningButton::press_action() { this->parent_->start_self_cleaning(); }
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/button/self_cleaning.h
Normal file
18
esphome/components/haier/button/self_cleaning.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/button/button.h"
|
||||||
|
#include "../hon_climate.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class SelfCleaningButton : public button::Button, public Parented<HonClimate> {
|
||||||
|
public:
|
||||||
|
SelfCleaningButton() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void press_action() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
9
esphome/components/haier/button/steri_cleaning.cpp
Normal file
9
esphome/components/haier/button/steri_cleaning.cpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#include "steri_cleaning.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void SteriCleaningButton::press_action() { this->parent_->start_steri_cleaning(); }
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/button/steri_cleaning.h
Normal file
18
esphome/components/haier/button/steri_cleaning.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/button/button.h"
|
||||||
|
#include "../hon_climate.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class SteriCleaningButton : public button::Button, public Parented<HonClimate> {
|
||||||
|
public:
|
||||||
|
SteriCleaningButton() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void press_action() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
|
@ -55,6 +55,7 @@ PROTOCOL_HON = "HON"
|
||||||
PROTOCOL_SMARTAIR2 = "SMARTAIR2"
|
PROTOCOL_SMARTAIR2 = "SMARTAIR2"
|
||||||
|
|
||||||
haier_ns = cg.esphome_ns.namespace("haier")
|
haier_ns = cg.esphome_ns.namespace("haier")
|
||||||
|
hon_protocol_ns = haier_ns.namespace("hon_protocol")
|
||||||
HaierClimateBase = haier_ns.class_(
|
HaierClimateBase = haier_ns.class_(
|
||||||
"HaierClimateBase", uart.UARTDevice, climate.Climate, cg.Component
|
"HaierClimateBase", uart.UARTDevice, climate.Climate, cg.Component
|
||||||
)
|
)
|
||||||
|
@ -63,7 +64,7 @@ Smartair2Climate = haier_ns.class_("Smartair2Climate", HaierClimateBase)
|
||||||
|
|
||||||
CONF_HAIER_ID = "haier_id"
|
CONF_HAIER_ID = "haier_id"
|
||||||
|
|
||||||
AirflowVerticalDirection = haier_ns.enum("AirflowVerticalDirection", True)
|
AirflowVerticalDirection = hon_protocol_ns.enum("VerticalSwingMode", True)
|
||||||
AIRFLOW_VERTICAL_DIRECTION_OPTIONS = {
|
AIRFLOW_VERTICAL_DIRECTION_OPTIONS = {
|
||||||
"HEALTH_UP": AirflowVerticalDirection.HEALTH_UP,
|
"HEALTH_UP": AirflowVerticalDirection.HEALTH_UP,
|
||||||
"MAX_UP": AirflowVerticalDirection.MAX_UP,
|
"MAX_UP": AirflowVerticalDirection.MAX_UP,
|
||||||
|
@ -73,7 +74,7 @@ AIRFLOW_VERTICAL_DIRECTION_OPTIONS = {
|
||||||
"HEALTH_DOWN": AirflowVerticalDirection.HEALTH_DOWN,
|
"HEALTH_DOWN": AirflowVerticalDirection.HEALTH_DOWN,
|
||||||
}
|
}
|
||||||
|
|
||||||
AirflowHorizontalDirection = haier_ns.enum("AirflowHorizontalDirection", True)
|
AirflowHorizontalDirection = hon_protocol_ns.enum("HorizontalSwingMode", True)
|
||||||
AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = {
|
AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = {
|
||||||
"MAX_LEFT": AirflowHorizontalDirection.MAX_LEFT,
|
"MAX_LEFT": AirflowHorizontalDirection.MAX_LEFT,
|
||||||
"LEFT": AirflowHorizontalDirection.LEFT,
|
"LEFT": AirflowHorizontalDirection.LEFT,
|
||||||
|
@ -483,4 +484,4 @@ async def to_code(config):
|
||||||
trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf
|
trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf
|
||||||
)
|
)
|
||||||
# https://github.com/paveldn/HaierProtocol
|
# https://github.com/paveldn/HaierProtocol
|
||||||
cg.add_library("pavlodn/HaierProtocol", "0.9.25")
|
cg.add_library("pavlodn/HaierProtocol", "0.9.28")
|
||||||
|
|
|
@ -234,6 +234,7 @@ void HaierClimateBase::setup() {
|
||||||
this->haier_protocol_.set_default_timeout_handler(
|
this->haier_protocol_.set_default_timeout_handler(
|
||||||
std::bind(&esphome::haier::HaierClimateBase::timeout_default_handler_, this, std::placeholders::_1));
|
std::bind(&esphome::haier::HaierClimateBase::timeout_default_handler_, this, std::placeholders::_1));
|
||||||
this->set_handlers();
|
this->set_handlers();
|
||||||
|
this->initialization();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HaierClimateBase::dump_config() {
|
void HaierClimateBase::dump_config() {
|
||||||
|
@ -326,7 +327,7 @@ ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||||
|
|
||||||
void HaierClimateBase::control(const ClimateCall &call) {
|
void HaierClimateBase::control(const ClimateCall &call) {
|
||||||
ESP_LOGD("Control", "Control call");
|
ESP_LOGD("Control", "Control call");
|
||||||
if (this->protocol_phase_ < ProtocolPhases::IDLE) {
|
if (!this->valid_connection()) {
|
||||||
ESP_LOGW(TAG, "Can't send control packet, first poll answer not received");
|
ESP_LOGW(TAG, "Can't send control packet, first poll answer not received");
|
||||||
return; // cancel the control, we cant do it without a poll answer.
|
return; // cancel the control, we cant do it without a poll answer.
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class HaierClimateBase : public esphome::Component,
|
||||||
void set_supported_modes(const std::set<esphome::climate::ClimateMode> &modes);
|
void set_supported_modes(const std::set<esphome::climate::ClimateMode> &modes);
|
||||||
void set_supported_swing_modes(const std::set<esphome::climate::ClimateSwingMode> &modes);
|
void set_supported_swing_modes(const std::set<esphome::climate::ClimateSwingMode> &modes);
|
||||||
void set_supported_presets(const std::set<esphome::climate::ClimatePreset> &presets);
|
void set_supported_presets(const std::set<esphome::climate::ClimatePreset> &presets);
|
||||||
bool valid_connection() { return this->protocol_phase_ >= ProtocolPhases::IDLE; };
|
bool valid_connection() const { return this->protocol_phase_ >= ProtocolPhases::IDLE; };
|
||||||
size_t available() noexcept override { return esphome::uart::UARTDevice::available(); };
|
size_t available() noexcept override { return esphome::uart::UARTDevice::available(); };
|
||||||
size_t read_array(uint8_t *data, size_t len) noexcept override {
|
size_t read_array(uint8_t *data, size_t len) noexcept override {
|
||||||
return esphome::uart::UARTDevice::read_array(data, len) ? len : 0;
|
return esphome::uart::UARTDevice::read_array(data, len) ? len : 0;
|
||||||
|
@ -80,6 +80,7 @@ class HaierClimateBase : public esphome::Component,
|
||||||
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
||||||
virtual haier_protocol::HaierMessage get_control_message() = 0;
|
virtual haier_protocol::HaierMessage get_control_message() = 0;
|
||||||
virtual haier_protocol::HaierMessage get_power_message(bool state) = 0;
|
virtual haier_protocol::HaierMessage get_power_message(bool state) = 0;
|
||||||
|
virtual void initialization(){};
|
||||||
virtual bool prepare_pending_action();
|
virtual bool prepare_pending_action();
|
||||||
virtual void process_protocol_reset();
|
virtual void process_protocol_reset();
|
||||||
esphome::climate::ClimateTraits traits() override;
|
esphome::climate::ClimateTraits traits() override;
|
||||||
|
|
|
@ -19,38 +19,6 @@ constexpr uint8_t CONTROL_MESSAGE_RETRIES = 5;
|
||||||
constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL = std::chrono::milliseconds(500);
|
constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL = std::chrono::milliseconds(500);
|
||||||
constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS = 600000;
|
constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS = 600000;
|
||||||
|
|
||||||
hon_protocol::VerticalSwingMode get_vertical_swing_mode(AirflowVerticalDirection direction) {
|
|
||||||
switch (direction) {
|
|
||||||
case AirflowVerticalDirection::HEALTH_UP:
|
|
||||||
return hon_protocol::VerticalSwingMode::HEALTH_UP;
|
|
||||||
case AirflowVerticalDirection::MAX_UP:
|
|
||||||
return hon_protocol::VerticalSwingMode::MAX_UP;
|
|
||||||
case AirflowVerticalDirection::UP:
|
|
||||||
return hon_protocol::VerticalSwingMode::UP;
|
|
||||||
case AirflowVerticalDirection::DOWN:
|
|
||||||
return hon_protocol::VerticalSwingMode::DOWN;
|
|
||||||
case AirflowVerticalDirection::HEALTH_DOWN:
|
|
||||||
return hon_protocol::VerticalSwingMode::HEALTH_DOWN;
|
|
||||||
default:
|
|
||||||
return hon_protocol::VerticalSwingMode::CENTER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hon_protocol::HorizontalSwingMode get_horizontal_swing_mode(AirflowHorizontalDirection direction) {
|
|
||||||
switch (direction) {
|
|
||||||
case AirflowHorizontalDirection::MAX_LEFT:
|
|
||||||
return hon_protocol::HorizontalSwingMode::MAX_LEFT;
|
|
||||||
case AirflowHorizontalDirection::LEFT:
|
|
||||||
return hon_protocol::HorizontalSwingMode::LEFT;
|
|
||||||
case AirflowHorizontalDirection::RIGHT:
|
|
||||||
return hon_protocol::HorizontalSwingMode::RIGHT;
|
|
||||||
case AirflowHorizontalDirection::MAX_RIGHT:
|
|
||||||
return hon_protocol::HorizontalSwingMode::MAX_RIGHT;
|
|
||||||
default:
|
|
||||||
return hon_protocol::HorizontalSwingMode::CENTER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HonClimate::HonClimate()
|
HonClimate::HonClimate()
|
||||||
: cleaning_status_(CleaningState::NO_CLEANING), got_valid_outdoor_temp_(false), active_alarms_{0x00, 0x00, 0x00,
|
: cleaning_status_(CleaningState::NO_CLEANING), got_valid_outdoor_temp_(false), active_alarms_{0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00,
|
||||||
|
@ -66,17 +34,21 @@ void HonClimate::set_beeper_state(bool state) { this->beeper_status_ = state; }
|
||||||
|
|
||||||
bool HonClimate::get_beeper_state() const { return this->beeper_status_; }
|
bool HonClimate::get_beeper_state() const { return this->beeper_status_; }
|
||||||
|
|
||||||
AirflowVerticalDirection HonClimate::get_vertical_airflow() const { return this->vertical_direction_; };
|
esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
|
||||||
|
return this->current_vertical_swing_;
|
||||||
|
};
|
||||||
|
|
||||||
void HonClimate::set_vertical_airflow(AirflowVerticalDirection direction) {
|
void HonClimate::set_vertical_airflow(hon_protocol::VerticalSwingMode direction) {
|
||||||
this->vertical_direction_ = direction;
|
this->pending_vertical_direction_ = direction;
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AirflowHorizontalDirection HonClimate::get_horizontal_airflow() const { return this->horizontal_direction_; }
|
esphome::optional<hon_protocol::HorizontalSwingMode> HonClimate::get_horizontal_airflow() const {
|
||||||
|
return this->current_horizontal_swing_;
|
||||||
|
}
|
||||||
|
|
||||||
void HonClimate::set_horizontal_airflow(AirflowHorizontalDirection direction) {
|
void HonClimate::set_horizontal_airflow(hon_protocol::HorizontalSwingMode direction) {
|
||||||
this->horizontal_direction_ = direction;
|
this->pending_horizontal_direction_ = direction;
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +120,11 @@ haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(haie
|
||||||
this->hvac_hardware_info_.value().hardware_version_ = std::string(tmp);
|
this->hvac_hardware_info_.value().hardware_version_ = std::string(tmp);
|
||||||
strncpy(tmp, answr->device_name, 8);
|
strncpy(tmp, answr->device_name, 8);
|
||||||
this->hvac_hardware_info_.value().device_name_ = std::string(tmp);
|
this->hvac_hardware_info_.value().device_name_ = std::string(tmp);
|
||||||
|
#ifdef USE_TEXT_SENSOR
|
||||||
|
this->update_sub_text_sensor_(SubTextSensorType::APPLIANCE_NAME, this->hvac_hardware_info_.value().device_name_);
|
||||||
|
this->update_sub_text_sensor_(SubTextSensorType::PROTOCOL_VERSION,
|
||||||
|
this->hvac_hardware_info_.value().protocol_version_);
|
||||||
|
#endif
|
||||||
this->hvac_hardware_info_.value().functions_[0] = (answr->functions[1] & 0x01) != 0; // interactive mode support
|
this->hvac_hardware_info_.value().functions_[0] = (answr->functions[1] & 0x01) != 0; // interactive mode support
|
||||||
this->hvac_hardware_info_.value().functions_[1] =
|
this->hvac_hardware_info_.value().functions_[1] =
|
||||||
(answr->functions[1] & 0x02) != 0; // controller-device mode support
|
(answr->functions[1] & 0x02) != 0; // controller-device mode support
|
||||||
|
@ -488,6 +465,19 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HonClimate::initialization() {
|
||||||
|
constexpr uint32_t restore_settings_version = 0xE834D8DCUL;
|
||||||
|
this->rtc_ = global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
||||||
|
HonSettings recovered;
|
||||||
|
if (this->rtc_.load(&recovered)) {
|
||||||
|
this->settings_ = recovered;
|
||||||
|
} else {
|
||||||
|
this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER};
|
||||||
|
}
|
||||||
|
this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
|
||||||
|
this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
|
||||||
|
}
|
||||||
|
|
||||||
haier_protocol::HaierMessage HonClimate::get_control_message() {
|
haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
|
uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
|
||||||
memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
|
memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
|
||||||
|
@ -560,16 +550,16 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
if (climate_control.swing_mode.has_value()) {
|
if (climate_control.swing_mode.has_value()) {
|
||||||
switch (climate_control.swing_mode.value()) {
|
switch (climate_control.swing_mode.value()) {
|
||||||
case CLIMATE_SWING_OFF:
|
case CLIMATE_SWING_OFF:
|
||||||
out_data->horizontal_swing_mode = (uint8_t) get_horizontal_swing_mode(this->horizontal_direction_);
|
out_data->horizontal_swing_mode = (uint8_t) this->settings_.last_horizontal_swing;
|
||||||
out_data->vertical_swing_mode = (uint8_t) get_vertical_swing_mode(this->vertical_direction_);
|
out_data->vertical_swing_mode = (uint8_t) this->settings_.last_vertiacal_swing;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_SWING_VERTICAL:
|
case CLIMATE_SWING_VERTICAL:
|
||||||
out_data->horizontal_swing_mode = (uint8_t) get_horizontal_swing_mode(this->horizontal_direction_);
|
out_data->horizontal_swing_mode = (uint8_t) this->settings_.last_horizontal_swing;
|
||||||
out_data->vertical_swing_mode = (uint8_t) hon_protocol::VerticalSwingMode::AUTO;
|
out_data->vertical_swing_mode = (uint8_t) hon_protocol::VerticalSwingMode::AUTO;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_SWING_HORIZONTAL:
|
case CLIMATE_SWING_HORIZONTAL:
|
||||||
out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::AUTO;
|
out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::AUTO;
|
||||||
out_data->vertical_swing_mode = (uint8_t) get_vertical_swing_mode(this->vertical_direction_);
|
out_data->vertical_swing_mode = (uint8_t) this->settings_.last_vertiacal_swing;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_SWING_BOTH:
|
case CLIMATE_SWING_BOTH:
|
||||||
out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::AUTO;
|
out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::AUTO;
|
||||||
|
@ -631,11 +621,14 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if (out_data->vertical_swing_mode != (uint8_t) hon_protocol::VerticalSwingMode::AUTO)
|
if (this->pending_vertical_direction_.has_value()) {
|
||||||
out_data->vertical_swing_mode = (uint8_t) get_vertical_swing_mode(this->vertical_direction_);
|
out_data->vertical_swing_mode = (uint8_t) this->pending_vertical_direction_.value();
|
||||||
if (out_data->horizontal_swing_mode != (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)
|
this->pending_vertical_direction_.reset();
|
||||||
out_data->horizontal_swing_mode = (uint8_t) get_horizontal_swing_mode(this->horizontal_direction_);
|
}
|
||||||
|
if (this->pending_horizontal_direction_.has_value()) {
|
||||||
|
out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value();
|
||||||
|
this->pending_horizontal_direction_.reset();
|
||||||
}
|
}
|
||||||
out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0;
|
out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0;
|
||||||
control_out_buffer[4] = 0; // This byte should be cleared before setting values
|
control_out_buffer[4] = 0; // This byte should be cleared before setting values
|
||||||
|
@ -737,6 +730,33 @@ void HonClimate::update_sub_binary_sensor_(SubBinarySensorType type, uint8_t val
|
||||||
}
|
}
|
||||||
#endif // USE_BINARY_SENSOR
|
#endif // USE_BINARY_SENSOR
|
||||||
|
|
||||||
|
#ifdef USE_TEXT_SENSOR
|
||||||
|
void HonClimate::set_sub_text_sensor(SubTextSensorType type, text_sensor::TextSensor *sens) {
|
||||||
|
this->sub_text_sensors_[(size_t) type] = sens;
|
||||||
|
switch (type) {
|
||||||
|
case SubTextSensorType::APPLIANCE_NAME:
|
||||||
|
if (this->hvac_hardware_info_.has_value())
|
||||||
|
sens->publish_state(this->hvac_hardware_info_.value().device_name_);
|
||||||
|
break;
|
||||||
|
case SubTextSensorType::PROTOCOL_VERSION:
|
||||||
|
if (this->hvac_hardware_info_.has_value())
|
||||||
|
sens->publish_state(this->hvac_hardware_info_.value().protocol_version_);
|
||||||
|
break;
|
||||||
|
case SubTextSensorType::CLEANING_STATUS:
|
||||||
|
sens->publish_state(this->get_cleaning_status_text());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::string &value) {
|
||||||
|
size_t index = (size_t) type;
|
||||||
|
if (this->sub_text_sensors_[index] != nullptr)
|
||||||
|
this->sub_text_sensors_[index]->publish_state(value);
|
||||||
|
}
|
||||||
|
#endif // USE_TEXT_SENSOR
|
||||||
|
|
||||||
haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
|
haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
|
||||||
size_t expected_size = 2 + sizeof(hon_protocol::HaierPacketControl) + sizeof(hon_protocol::HaierPacketSensors) +
|
size_t expected_size = 2 + sizeof(hon_protocol::HaierPacketControl) + sizeof(hon_protocol::HaierPacketSensors) +
|
||||||
this->extra_control_packet_bytes_;
|
this->extra_control_packet_bytes_;
|
||||||
|
@ -896,6 +916,9 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
PendingAction({ActionRequest::TURN_POWER_OFF, esphome::optional<haier_protocol::HaierMessage>()});
|
PendingAction({ActionRequest::TURN_POWER_OFF, esphome::optional<haier_protocol::HaierMessage>()});
|
||||||
}
|
}
|
||||||
this->cleaning_status_ = new_cleaning;
|
this->cleaning_status_ = new_cleaning;
|
||||||
|
#ifdef USE_TEXT_SENSOR
|
||||||
|
this->update_sub_text_sensor_(SubTextSensorType::CLEANING_STATUS, this->get_cleaning_status_text());
|
||||||
|
#endif // USE_TEXT_SENSOR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -941,6 +964,19 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
this->swing_mode = CLIMATE_SWING_OFF;
|
this->swing_mode = CLIMATE_SWING_OFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Saving last known non auto mode for vertical and horizontal swing
|
||||||
|
this->current_vertical_swing_ = (hon_protocol::VerticalSwingMode) packet.control.vertical_swing_mode;
|
||||||
|
this->current_horizontal_swing_ = (hon_protocol::HorizontalSwingMode) packet.control.horizontal_swing_mode;
|
||||||
|
bool save_settings = ((this->current_vertical_swing_.value() != hon_protocol::VerticalSwingMode::AUTO) &&
|
||||||
|
(this->current_vertical_swing_.value() != hon_protocol::VerticalSwingMode::AUTO_SPECIAL) &&
|
||||||
|
(this->current_vertical_swing_.value() != this->settings_.last_vertiacal_swing)) ||
|
||||||
|
((this->current_horizontal_swing_.value() != hon_protocol::HorizontalSwingMode::AUTO) &&
|
||||||
|
(this->current_horizontal_swing_.value() != this->settings_.last_horizontal_swing));
|
||||||
|
if (save_settings) {
|
||||||
|
this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value();
|
||||||
|
this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value();
|
||||||
|
this->rtc_.save(&this->settings_);
|
||||||
|
}
|
||||||
should_publish = should_publish || (old_swing_mode != this->swing_mode);
|
should_publish = should_publish || (old_swing_mode != this->swing_mode);
|
||||||
}
|
}
|
||||||
this->last_valid_status_timestamp_ = std::chrono::steady_clock::now();
|
this->last_valid_status_timestamp_ = std::chrono::steady_clock::now();
|
||||||
|
|
|
@ -7,29 +7,16 @@
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_TEXT_SENSOR
|
||||||
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
|
#endif
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "haier_base.h"
|
#include "haier_base.h"
|
||||||
|
#include "hon_packet.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace haier {
|
namespace haier {
|
||||||
|
|
||||||
enum class AirflowVerticalDirection : uint8_t {
|
|
||||||
HEALTH_UP = 0,
|
|
||||||
MAX_UP = 1,
|
|
||||||
UP = 2,
|
|
||||||
CENTER = 3,
|
|
||||||
DOWN = 4,
|
|
||||||
HEALTH_DOWN = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class AirflowHorizontalDirection : uint8_t {
|
|
||||||
MAX_LEFT = 0,
|
|
||||||
LEFT = 1,
|
|
||||||
CENTER = 2,
|
|
||||||
RIGHT = 3,
|
|
||||||
MAX_RIGHT = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CleaningState : uint8_t {
|
enum class CleaningState : uint8_t {
|
||||||
NO_CLEANING = 0,
|
NO_CLEANING = 0,
|
||||||
SELF_CLEAN = 1,
|
SELF_CLEAN = 1,
|
||||||
|
@ -38,6 +25,11 @@ enum class CleaningState : uint8_t {
|
||||||
|
|
||||||
enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE_PARAMETER };
|
enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE_PARAMETER };
|
||||||
|
|
||||||
|
struct HonSettings {
|
||||||
|
hon_protocol::VerticalSwingMode last_vertiacal_swing;
|
||||||
|
hon_protocol::HorizontalSwingMode last_horizontal_swing;
|
||||||
|
};
|
||||||
|
|
||||||
class HonClimate : public HaierClimateBase {
|
class HonClimate : public HaierClimateBase {
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
public:
|
public:
|
||||||
|
@ -80,6 +72,20 @@ class HonClimate : public HaierClimateBase {
|
||||||
protected:
|
protected:
|
||||||
void update_sub_binary_sensor_(SubBinarySensorType type, uint8_t value);
|
void update_sub_binary_sensor_(SubBinarySensorType type, uint8_t value);
|
||||||
binary_sensor::BinarySensor *sub_binary_sensors_[(size_t) SubBinarySensorType::SUB_BINARY_SENSOR_TYPE_COUNT]{nullptr};
|
binary_sensor::BinarySensor *sub_binary_sensors_[(size_t) SubBinarySensorType::SUB_BINARY_SENSOR_TYPE_COUNT]{nullptr};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TEXT_SENSOR
|
||||||
|
public:
|
||||||
|
enum class SubTextSensorType {
|
||||||
|
CLEANING_STATUS = 0,
|
||||||
|
PROTOCOL_VERSION,
|
||||||
|
APPLIANCE_NAME,
|
||||||
|
SUB_TEXT_SENSOR_TYPE_COUNT,
|
||||||
|
};
|
||||||
|
void set_sub_text_sensor(SubTextSensorType type, text_sensor::TextSensor *sens);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
|
||||||
|
text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
HonClimate();
|
HonClimate();
|
||||||
|
@ -89,10 +95,10 @@ class HonClimate : public HaierClimateBase {
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_beeper_state(bool state);
|
void set_beeper_state(bool state);
|
||||||
bool get_beeper_state() const;
|
bool get_beeper_state() const;
|
||||||
AirflowVerticalDirection get_vertical_airflow() const;
|
esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const;
|
||||||
void set_vertical_airflow(AirflowVerticalDirection direction);
|
void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
|
||||||
AirflowHorizontalDirection get_horizontal_airflow() const;
|
esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
|
||||||
void set_horizontal_airflow(AirflowHorizontalDirection direction);
|
void set_horizontal_airflow(hon_protocol::HorizontalSwingMode direction);
|
||||||
std::string get_cleaning_status_text() const;
|
std::string get_cleaning_status_text() const;
|
||||||
CleaningState get_cleaning_status() const;
|
CleaningState get_cleaning_status() const;
|
||||||
void start_self_cleaning();
|
void start_self_cleaning();
|
||||||
|
@ -108,6 +114,7 @@ class HonClimate : public HaierClimateBase {
|
||||||
void process_phase(std::chrono::steady_clock::time_point now) override;
|
void process_phase(std::chrono::steady_clock::time_point now) override;
|
||||||
haier_protocol::HaierMessage get_control_message() override;
|
haier_protocol::HaierMessage get_control_message() override;
|
||||||
haier_protocol::HaierMessage get_power_message(bool state) override;
|
haier_protocol::HaierMessage get_power_message(bool state) override;
|
||||||
|
void initialization() override;
|
||||||
bool prepare_pending_action() override;
|
bool prepare_pending_action() override;
|
||||||
void process_protocol_reset() override;
|
void process_protocol_reset() override;
|
||||||
bool should_get_big_data_();
|
bool should_get_big_data_();
|
||||||
|
@ -147,9 +154,9 @@ class HonClimate : public HaierClimateBase {
|
||||||
bool beeper_status_;
|
bool beeper_status_;
|
||||||
CleaningState cleaning_status_;
|
CleaningState cleaning_status_;
|
||||||
bool got_valid_outdoor_temp_;
|
bool got_valid_outdoor_temp_;
|
||||||
AirflowVerticalDirection vertical_direction_;
|
esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{};
|
||||||
AirflowHorizontalDirection horizontal_direction_;
|
esphome::optional<hon_protocol::HorizontalSwingMode> pending_horizontal_direction_{};
|
||||||
esphome::optional<HardwareInfo> hvac_hardware_info_;
|
esphome::optional<HardwareInfo> hvac_hardware_info_{};
|
||||||
uint8_t active_alarms_[8];
|
uint8_t active_alarms_[8];
|
||||||
int extra_control_packet_bytes_;
|
int extra_control_packet_bytes_;
|
||||||
HonControlMethod control_method_;
|
HonControlMethod control_method_;
|
||||||
|
@ -159,6 +166,10 @@ class HonClimate : public HaierClimateBase {
|
||||||
float active_alarm_count_{NAN};
|
float active_alarm_count_{NAN};
|
||||||
std::chrono::steady_clock::time_point last_alarm_request_;
|
std::chrono::steady_clock::time_point last_alarm_request_;
|
||||||
int big_data_sensors_{0};
|
int big_data_sensors_{0};
|
||||||
|
esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{};
|
||||||
|
esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
|
||||||
|
HonSettings settings_;
|
||||||
|
ESPPreferenceObject rtc_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
|
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
|
||||||
|
|
|
@ -13,7 +13,10 @@ enum class VerticalSwingMode : uint8_t {
|
||||||
UP = 0x04,
|
UP = 0x04,
|
||||||
CENTER = 0x06,
|
CENTER = 0x06,
|
||||||
DOWN = 0x08,
|
DOWN = 0x08,
|
||||||
AUTO = 0x0C
|
MAX_DOWN = 0x0A,
|
||||||
|
AUTO = 0x0C,
|
||||||
|
// Auto for special modes
|
||||||
|
AUTO_SPECIAL = 0x0E
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class HorizontalSwingMode : uint8_t {
|
enum class HorizontalSwingMode : uint8_t {
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ..climate import (
|
||||||
HonClimate,
|
HonClimate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@paveldn"]
|
||||||
SensorTypeEnum = HonClimate.enum("SubSensorType", True)
|
SensorTypeEnum = HonClimate.enum("SubSensorType", True)
|
||||||
|
|
||||||
# Haier sensors
|
# Haier sensors
|
||||||
|
|
54
esphome/components/haier/text_sensor/__init__.py
Normal file
54
esphome/components/haier/text_sensor/__init__.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import text_sensor
|
||||||
|
from esphome.const import (
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
ENTITY_CATEGORY_NONE,
|
||||||
|
)
|
||||||
|
from ..climate import (
|
||||||
|
CONF_HAIER_ID,
|
||||||
|
HonClimate,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@paveldn"]
|
||||||
|
TextSensorTypeEnum = HonClimate.enum("SubTextSensorType", True)
|
||||||
|
|
||||||
|
# Haier text sensors
|
||||||
|
CONF_CLEANING_STATUS = "cleaning_status"
|
||||||
|
CONF_PROTOCOL_VERSION = "protocol_version"
|
||||||
|
CONF_APPLIANCE_NAME = "appliance_name"
|
||||||
|
|
||||||
|
# Additional icons
|
||||||
|
ICON_SPRAY_BOTTLE = "mdi:spray-bottle"
|
||||||
|
ICON_TEXT_BOX = "mdi:text-box-outline"
|
||||||
|
|
||||||
|
TEXT_SENSOR_TYPES = {
|
||||||
|
CONF_CLEANING_STATUS: text_sensor.text_sensor_schema(
|
||||||
|
icon=ICON_SPRAY_BOTTLE,
|
||||||
|
entity_category=ENTITY_CATEGORY_NONE,
|
||||||
|
),
|
||||||
|
CONF_PROTOCOL_VERSION: text_sensor.text_sensor_schema(
|
||||||
|
icon=ICON_TEXT_BOX,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
CONF_APPLIANCE_NAME: text_sensor.text_sensor_schema(
|
||||||
|
icon=ICON_TEXT_BOX,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_HAIER_ID): cv.use_id(HonClimate),
|
||||||
|
}
|
||||||
|
).extend({cv.Optional(type): schema for type, schema in TEXT_SENSOR_TYPES.items()})
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
paren = await cg.get_variable(config[CONF_HAIER_ID])
|
||||||
|
|
||||||
|
for type, _ in TEXT_SENSOR_TYPES.items():
|
||||||
|
if conf := config.get(type):
|
||||||
|
sens = await text_sensor.new_text_sensor(conf)
|
||||||
|
text_sensor_type = getattr(TextSensorTypeEnum, type.upper())
|
||||||
|
cg.add(paren.set_sub_text_sensor(text_sensor_type, sens))
|
|
@ -1,19 +1,32 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
from esphome import pins
|
||||||
from esphome.components import i2c
|
from esphome.components import i2c
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import (
|
||||||
|
CONF_BINARY_SENSOR,
|
||||||
|
CONF_CHANNEL,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_INVERTED,
|
||||||
|
CONF_MODE,
|
||||||
|
CONF_NUMBER,
|
||||||
|
CONF_OUTPUT,
|
||||||
|
)
|
||||||
|
|
||||||
CONF_TOUCH_THRESHOLD = "touch_threshold"
|
CONF_TOUCH_THRESHOLD = "touch_threshold"
|
||||||
CONF_RELEASE_THRESHOLD = "release_threshold"
|
CONF_RELEASE_THRESHOLD = "release_threshold"
|
||||||
CONF_TOUCH_DEBOUNCE = "touch_debounce"
|
CONF_TOUCH_DEBOUNCE = "touch_debounce"
|
||||||
CONF_RELEASE_DEBOUNCE = "release_debounce"
|
CONF_RELEASE_DEBOUNCE = "release_debounce"
|
||||||
|
CONF_MAX_TOUCH_CHANNEL = "max_touch_channel"
|
||||||
|
CONF_MPR121 = "mpr121"
|
||||||
|
CONF_MPR121_ID = "mpr121_id"
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
AUTO_LOAD = ["binary_sensor"]
|
|
||||||
|
|
||||||
mpr121_ns = cg.esphome_ns.namespace("mpr121")
|
mpr121_ns = cg.esphome_ns.namespace("mpr121")
|
||||||
CONF_MPR121_ID = "mpr121_id"
|
|
||||||
MPR121Component = mpr121_ns.class_("MPR121Component", cg.Component, i2c.I2CDevice)
|
MPR121Component = mpr121_ns.class_("MPR121Component", cg.Component, i2c.I2CDevice)
|
||||||
|
MPR121GPIOPin = mpr121_ns.class_("MPR121GPIOPin", cg.GPIOPin)
|
||||||
|
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
|
@ -28,6 +41,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Optional(CONF_RELEASE_THRESHOLD, default=0x06): cv.int_range(
|
cv.Optional(CONF_RELEASE_THRESHOLD, default=0x06): cv.int_range(
|
||||||
min=0x05, max=0x30
|
min=0x05, max=0x30
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_MAX_TOUCH_CHANNEL): cv.int_range(min=3, max=11),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
@ -35,11 +49,79 @@ CONFIG_SCHEMA = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _final_validate(config):
|
||||||
|
fconf = fv.full_config.get()
|
||||||
|
max_touch_channel = 3
|
||||||
|
if (binary_sensors := fconf.get(CONF_BINARY_SENSOR)) is not None:
|
||||||
|
for binary_sensor in binary_sensors:
|
||||||
|
if binary_sensor.get(CONF_MPR121_ID) == config[CONF_ID]:
|
||||||
|
max_touch_channel = max(max_touch_channel, binary_sensor[CONF_CHANNEL])
|
||||||
|
if max_touch_channel_in_config := config.get(CONF_MAX_TOUCH_CHANNEL):
|
||||||
|
if max_touch_channel != max_touch_channel_in_config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
"Max touch channel must equal the highest binary sensor channel or be removed for auto calculation",
|
||||||
|
path=[CONF_MAX_TOUCH_CHANNEL],
|
||||||
|
)
|
||||||
|
path = fconf.get_path_for_id(config[CONF_ID])[:-1]
|
||||||
|
this_config = fconf.get_config_for_path(path)
|
||||||
|
this_config[CONF_MAX_TOUCH_CHANNEL] = max_touch_channel
|
||||||
|
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
cg.add(var.set_touch_debounce(config[CONF_TOUCH_DEBOUNCE]))
|
cg.add(var.set_touch_debounce(config[CONF_TOUCH_DEBOUNCE]))
|
||||||
cg.add(var.set_release_debounce(config[CONF_RELEASE_DEBOUNCE]))
|
cg.add(var.set_release_debounce(config[CONF_RELEASE_DEBOUNCE]))
|
||||||
cg.add(var.set_touch_threshold(config[CONF_TOUCH_THRESHOLD]))
|
cg.add(var.set_touch_threshold(config[CONF_TOUCH_THRESHOLD]))
|
||||||
cg.add(var.set_release_threshold(config[CONF_RELEASE_THRESHOLD]))
|
cg.add(var.set_release_threshold(config[CONF_RELEASE_THRESHOLD]))
|
||||||
|
cg.add(var.set_max_touch_channel(config[CONF_MAX_TOUCH_CHANNEL]))
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_mode(value):
|
||||||
|
if bool(value[CONF_INPUT]) == bool(value[CONF_OUTPUT]):
|
||||||
|
raise cv.Invalid("Mode must be either input or output")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# https://www.nxp.com/docs/en/data-sheet/MPR121.pdf, page 4
|
||||||
|
#
|
||||||
|
# Among the 12 electrode inputs, 8 inputs are designed as multifunctional pins. When these pins are
|
||||||
|
# not configured as electrodes, they may be used to drive LEDs or used for general purpose input or
|
||||||
|
# output.
|
||||||
|
MPR121_GPIO_PIN_SCHEMA = pins.gpio_base_schema(
|
||||||
|
MPR121GPIOPin,
|
||||||
|
cv.int_range(min=4, max=11),
|
||||||
|
modes=[CONF_INPUT, CONF_OUTPUT],
|
||||||
|
mode_validator=validate_mode,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_MPR121): cv.use_id(MPR121Component),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def mpr121_pin_final_validate(pin_config, parent_config):
|
||||||
|
if pin_config[CONF_NUMBER] <= parent_config[CONF_MAX_TOUCH_CHANNEL]:
|
||||||
|
raise cv.Invalid(
|
||||||
|
"Pin number must be higher than the max touch channel of the MPR121 component",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pins.PIN_SCHEMA_REGISTRY.register(
|
||||||
|
CONF_MPR121, MPR121_GPIO_PIN_SCHEMA, mpr121_pin_final_validate
|
||||||
|
)
|
||||||
|
async def mpr121_gpio_pin_to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
parent = await cg.get_variable(config[CONF_MPR121])
|
||||||
|
|
||||||
|
cg.add(var.set_parent(parent))
|
||||||
|
|
||||||
|
num = config[CONF_NUMBER]
|
||||||
|
cg.add(var.set_pin(num))
|
||||||
|
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||||
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
|
return var
|
||||||
|
|
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import binary_sensor
|
from esphome.components import binary_sensor
|
||||||
from esphome.const import CONF_CHANNEL
|
from esphome.const import CONF_CHANNEL
|
||||||
from . import (
|
from .. import (
|
||||||
mpr121_ns,
|
mpr121_ns,
|
||||||
MPR121Component,
|
MPR121Component,
|
||||||
CONF_MPR121_ID,
|
CONF_MPR121_ID,
|
||||||
|
@ -11,9 +11,9 @@ from . import (
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["mpr121"]
|
DEPENDENCIES = ["mpr121"]
|
||||||
MPR121Channel = mpr121_ns.class_("MPR121Channel", binary_sensor.BinarySensor)
|
MPR121BinarySensor = mpr121_ns.class_("MPR121BinarySensor", binary_sensor.BinarySensor)
|
||||||
|
|
||||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(MPR121Channel).extend(
|
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(MPR121BinarySensor).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_MPR121_ID): cv.use_id(MPR121Component),
|
cv.GenerateID(CONF_MPR121_ID): cv.use_id(MPR121Component),
|
||||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=11),
|
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=11),
|
||||||
|
@ -27,6 +27,7 @@ async def to_code(config):
|
||||||
var = await binary_sensor.new_binary_sensor(config)
|
var = await binary_sensor.new_binary_sensor(config)
|
||||||
hub = await cg.get_variable(config[CONF_MPR121_ID])
|
hub = await cg.get_variable(config[CONF_MPR121_ID])
|
||||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||||
|
cg.register_parented(var, hub)
|
||||||
|
|
||||||
if CONF_TOUCH_THRESHOLD in config:
|
if CONF_TOUCH_THRESHOLD in config:
|
||||||
cg.add(var.set_touch_threshold(config[CONF_TOUCH_THRESHOLD]))
|
cg.add(var.set_touch_threshold(config[CONF_TOUCH_THRESHOLD]))
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "mpr121_binary_sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace mpr121 {
|
||||||
|
|
||||||
|
void MPR121BinarySensor::setup() {
|
||||||
|
uint8_t touch_threshold = this->touch_threshold_.value_or(this->parent_->get_touch_threshold());
|
||||||
|
this->parent_->write_byte(MPR121_TOUCHTH_0 + 2 * this->channel_, touch_threshold);
|
||||||
|
|
||||||
|
uint8_t release_threshold = this->release_threshold_.value_or(this->parent_->get_release_threshold());
|
||||||
|
this->parent_->write_byte(MPR121_RELEASETH_0 + 2 * this->channel_, release_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPR121BinarySensor::process(uint16_t data) {
|
||||||
|
bool new_state = data & (1 << this->channel_);
|
||||||
|
this->publish_state(new_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mpr121
|
||||||
|
} // namespace esphome
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
|
||||||
|
#include "../mpr121.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace mpr121 {
|
||||||
|
|
||||||
|
class MPR121BinarySensor : public binary_sensor::BinarySensor, public MPR121Channel, public Parented<MPR121Component> {
|
||||||
|
public:
|
||||||
|
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||||
|
void set_touch_threshold(uint8_t touch_threshold) { this->touch_threshold_ = touch_threshold; };
|
||||||
|
void set_release_threshold(uint8_t release_threshold) { this->release_threshold_ = release_threshold; };
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void process(uint16_t data) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t channel_{0};
|
||||||
|
optional<uint8_t> touch_threshold_{};
|
||||||
|
optional<uint8_t> release_threshold_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mpr121
|
||||||
|
} // namespace esphome
|
|
@ -1,6 +1,9 @@
|
||||||
#include "mpr121.h"
|
#include "mpr121.h"
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace mpr121 {
|
namespace mpr121 {
|
||||||
|
@ -20,10 +23,7 @@ void MPR121Component::setup() {
|
||||||
|
|
||||||
// set touch sensitivity for all 12 channels
|
// set touch sensitivity for all 12 channels
|
||||||
for (auto *channel : this->channels_) {
|
for (auto *channel : this->channels_) {
|
||||||
this->write_byte(MPR121_TOUCHTH_0 + 2 * channel->channel_,
|
channel->setup();
|
||||||
channel->touch_threshold_.value_or(this->touch_threshold_));
|
|
||||||
this->write_byte(MPR121_RELEASETH_0 + 2 * channel->channel_,
|
|
||||||
channel->release_threshold_.value_or(this->release_threshold_));
|
|
||||||
}
|
}
|
||||||
this->write_byte(MPR121_MHDR, 0x01);
|
this->write_byte(MPR121_MHDR, 0x01);
|
||||||
this->write_byte(MPR121_NHDR, 0x01);
|
this->write_byte(MPR121_NHDR, 0x01);
|
||||||
|
@ -44,8 +44,15 @@ void MPR121Component::setup() {
|
||||||
this->write_byte(MPR121_CONFIG1, 0x10);
|
this->write_byte(MPR121_CONFIG1, 0x10);
|
||||||
// 0.5uS encoding, 1ms period
|
// 0.5uS encoding, 1ms period
|
||||||
this->write_byte(MPR121_CONFIG2, 0x20);
|
this->write_byte(MPR121_CONFIG2, 0x20);
|
||||||
// start with first 5 bits of baseline tracking
|
|
||||||
this->write_byte(MPR121_ECR, 0x8F);
|
// Write the Electrode Configuration Register
|
||||||
|
// * Highest 2 bits is "Calibration Lock", which we set to a value corresponding to 5 bits.
|
||||||
|
// * The 2 bits below is "Proximity Enable" and are left at 0.
|
||||||
|
// * The 4 least significant bits control how many electrodes are enabled. Electrodes are enabled
|
||||||
|
// as a range, starting at 0 up to the highest channel index used.
|
||||||
|
this->write_byte(MPR121_ECR, 0x80 | (this->max_touch_channel_ + 1));
|
||||||
|
|
||||||
|
this->flush_gpio_();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPR121Component::set_touch_debounce(uint8_t debounce) {
|
void MPR121Component::set_touch_debounce(uint8_t debounce) {
|
||||||
|
@ -86,6 +93,72 @@ void MPR121Component::loop() {
|
||||||
|
|
||||||
for (auto *channel : this->channels_)
|
for (auto *channel : this->channels_)
|
||||||
channel->process(val);
|
channel->process(val);
|
||||||
|
|
||||||
|
this->read_byte(MPR121_GPIODATA, &this->gpio_input_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MPR121Component::digital_read(uint8_t ionum) { return (this->gpio_input_ & (1 << ionum)) != 0; }
|
||||||
|
|
||||||
|
void MPR121Component::digital_write(uint8_t ionum, bool value) {
|
||||||
|
if (value) {
|
||||||
|
this->gpio_output_ |= (1 << ionum);
|
||||||
|
} else {
|
||||||
|
this->gpio_output_ &= ~(1 << ionum);
|
||||||
|
}
|
||||||
|
this->flush_gpio_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPR121Component::pin_mode(uint8_t ionum, gpio::Flags flags) {
|
||||||
|
this->gpio_enable_ |= (1 << ionum);
|
||||||
|
if (flags & gpio::FLAG_INPUT) {
|
||||||
|
this->gpio_direction_ &= ~(1 << ionum);
|
||||||
|
} else if (flags & gpio::FLAG_OUTPUT) {
|
||||||
|
this->gpio_direction_ |= 1 << ionum;
|
||||||
|
}
|
||||||
|
this->flush_gpio_();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MPR121Component::flush_gpio_() {
|
||||||
|
if (this->is_failed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The CTL registers can configure internal pullup/pulldown resistors.
|
||||||
|
this->write_byte(MPR121_GPIOCTL0, 0x00);
|
||||||
|
this->write_byte(MPR121_GPIOCTL1, 0x00);
|
||||||
|
this->write_byte(MPR121_GPIOEN, this->gpio_enable_);
|
||||||
|
this->write_byte(MPR121_GPIODIR, this->gpio_direction_);
|
||||||
|
|
||||||
|
if (!this->write_byte(MPR121_GPIODATA, this->gpio_output_)) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPR121GPIOPin::setup() { this->pin_mode(this->flags_); }
|
||||||
|
|
||||||
|
void MPR121GPIOPin::pin_mode(gpio::Flags flags) {
|
||||||
|
assert(this->pin_ >= 4);
|
||||||
|
this->parent_->pin_mode(this->pin_ - 4, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MPR121GPIOPin::digital_read() {
|
||||||
|
assert(this->pin_ >= 4);
|
||||||
|
return this->parent_->digital_read(this->pin_ - 4) != this->inverted_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPR121GPIOPin::digital_write(bool value) {
|
||||||
|
assert(this->pin_ >= 4);
|
||||||
|
this->parent_->digital_write(this->pin_ - 4, value != this->inverted_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MPR121GPIOPin::dump_summary() const {
|
||||||
|
char buffer[32];
|
||||||
|
snprintf(buffer, sizeof(buffer), "ELE%u on MPR121", this->pin_);
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mpr121
|
} // namespace mpr121
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -39,6 +41,9 @@ enum {
|
||||||
MPR121_UPLIMIT = 0x7D,
|
MPR121_UPLIMIT = 0x7D,
|
||||||
MPR121_LOWLIMIT = 0x7E,
|
MPR121_LOWLIMIT = 0x7E,
|
||||||
MPR121_TARGETLIMIT = 0x7F,
|
MPR121_TARGETLIMIT = 0x7F,
|
||||||
|
MPR121_GPIOCTL0 = 0x73,
|
||||||
|
MPR121_GPIOCTL1 = 0x74,
|
||||||
|
MPR121_GPIODATA = 0x75,
|
||||||
MPR121_GPIODIR = 0x76,
|
MPR121_GPIODIR = 0x76,
|
||||||
MPR121_GPIOEN = 0x77,
|
MPR121_GPIOEN = 0x77,
|
||||||
MPR121_GPIOSET = 0x78,
|
MPR121_GPIOSET = 0x78,
|
||||||
|
@ -47,19 +52,10 @@ enum {
|
||||||
MPR121_SOFTRESET = 0x80,
|
MPR121_SOFTRESET = 0x80,
|
||||||
};
|
};
|
||||||
|
|
||||||
class MPR121Channel : public binary_sensor::BinarySensor {
|
class MPR121Channel {
|
||||||
friend class MPR121Component;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
virtual void setup() = 0;
|
||||||
void process(uint16_t data) { this->publish_state(static_cast<bool>(data & (1 << this->channel_))); }
|
virtual void process(uint16_t data) = 0;
|
||||||
void set_touch_threshold(uint8_t touch_threshold) { this->touch_threshold_ = touch_threshold; };
|
|
||||||
void set_release_threshold(uint8_t release_threshold) { this->release_threshold_ = release_threshold; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
uint8_t channel_{0};
|
|
||||||
optional<uint8_t> touch_threshold_{};
|
|
||||||
optional<uint8_t> release_threshold_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MPR121Component : public Component, public i2c::I2CDevice {
|
class MPR121Component : public Component, public i2c::I2CDevice {
|
||||||
|
@ -69,23 +65,63 @@ class MPR121Component : public Component, public i2c::I2CDevice {
|
||||||
void set_release_debounce(uint8_t debounce);
|
void set_release_debounce(uint8_t debounce);
|
||||||
void set_touch_threshold(uint8_t touch_threshold) { this->touch_threshold_ = touch_threshold; };
|
void set_touch_threshold(uint8_t touch_threshold) { this->touch_threshold_ = touch_threshold; };
|
||||||
void set_release_threshold(uint8_t release_threshold) { this->release_threshold_ = release_threshold; };
|
void set_release_threshold(uint8_t release_threshold) { this->release_threshold_ = release_threshold; };
|
||||||
uint8_t get_touch_threshold() { return this->touch_threshold_; };
|
uint8_t get_touch_threshold() const { return this->touch_threshold_; };
|
||||||
uint8_t get_release_threshold() { return this->release_threshold_; };
|
uint8_t get_release_threshold() const { return this->release_threshold_; };
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
float get_setup_priority() const override { return setup_priority::IO; }
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
|
void set_max_touch_channel(uint8_t max_touch_channel) { this->max_touch_channel_ = max_touch_channel; }
|
||||||
|
|
||||||
|
// GPIO helper functions.
|
||||||
|
bool digital_read(uint8_t ionum);
|
||||||
|
void digital_write(uint8_t ionum, bool value);
|
||||||
|
void pin_mode(uint8_t ionum, gpio::Flags flags);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<MPR121Channel *> channels_{};
|
std::vector<MPR121Channel *> channels_{};
|
||||||
uint8_t debounce_{0};
|
uint8_t debounce_{0};
|
||||||
uint8_t touch_threshold_{};
|
uint8_t touch_threshold_{};
|
||||||
uint8_t release_threshold_{};
|
uint8_t release_threshold_{};
|
||||||
|
uint8_t max_touch_channel_{3};
|
||||||
enum ErrorCode {
|
enum ErrorCode {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
COMMUNICATION_FAILED,
|
COMMUNICATION_FAILED,
|
||||||
WRONG_CHIP_STATE,
|
WRONG_CHIP_STATE,
|
||||||
} error_code_{NONE};
|
} error_code_{NONE};
|
||||||
|
|
||||||
|
bool flush_gpio_();
|
||||||
|
|
||||||
|
/// The enable mask - zero means high Z, 1 means GPIO usage
|
||||||
|
uint8_t gpio_enable_{0x00};
|
||||||
|
/// Mask for the pin mode - 1 means output, 0 means input
|
||||||
|
uint8_t gpio_direction_{0x00};
|
||||||
|
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
||||||
|
uint8_t gpio_output_{0x00};
|
||||||
|
/// The mask to read as input state - 1 means HIGH, 0 means LOW
|
||||||
|
uint8_t gpio_input_{0x00};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper class to expose a MPR121 pin as an internal input GPIO pin.
|
||||||
|
class MPR121GPIOPin : public GPIOPin {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void pin_mode(gpio::Flags flags) override;
|
||||||
|
bool digital_read() override;
|
||||||
|
void digital_write(bool value) override;
|
||||||
|
std::string dump_summary() const override;
|
||||||
|
|
||||||
|
void set_parent(MPR121Component *parent) { this->parent_ = parent; }
|
||||||
|
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||||
|
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||||
|
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
MPR121Component *parent_;
|
||||||
|
uint8_t pin_;
|
||||||
|
bool inverted_;
|
||||||
|
gpio::Flags flags_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mpr121
|
} // namespace mpr121
|
||||||
|
|
|
@ -952,6 +952,73 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||||
*/
|
*/
|
||||||
bool set_protocol_reparse_mode(bool active_mode);
|
bool set_protocol_reparse_mode(bool active_mode);
|
||||||
|
|
||||||
|
// ======== Nextion Intelligent Series ========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the video id of a component.
|
||||||
|
* @param component The component name.
|
||||||
|
* @param vid_id The video ID.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```cpp
|
||||||
|
* it.set_component_vid("textview", 1);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This will change the video id of the component `textview`.
|
||||||
|
*
|
||||||
|
* Note: Requires Nextion Intelligent series display.
|
||||||
|
*/
|
||||||
|
void set_component_vid(const char *component, uint8_t vid_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the drag availability of a component.
|
||||||
|
* @param component The component name.
|
||||||
|
* @param drag False: Drag not available, True: Drag available.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```cpp
|
||||||
|
* it.set_component_drag("textview", true);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This will enable drag to the component `textview`.
|
||||||
|
*
|
||||||
|
* Note: Requires Nextion Intelligent series display.
|
||||||
|
*/
|
||||||
|
void set_component_drag(const char *component, bool drag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the opaqueness (fading) of a component.
|
||||||
|
* @param component The component name.
|
||||||
|
* @param aph An integer between 0 and 127 related to the opaqueness/fading level.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```cpp
|
||||||
|
* it.set_component_aph("textview", 64);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This will set the opaqueness level of the component `textview` to 64.
|
||||||
|
*
|
||||||
|
* Note: Requires Nextion Intelligent series display.
|
||||||
|
*/
|
||||||
|
void set_component_aph(const char *component, uint8_t aph);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the position of a component.
|
||||||
|
* @param component The component name.
|
||||||
|
* @param x The new X (horizontal) coordinate for the component.
|
||||||
|
* @param y The new Y (vertical) coordinate for the component.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```cpp
|
||||||
|
* it.set_component_aph("textview", 64, 35);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This will move the component `textview` to the column 64 of row 35 of the display.
|
||||||
|
*
|
||||||
|
* Note: Requires Nextion Intelligent series display.
|
||||||
|
*/
|
||||||
|
void set_component_position(const char *component, uint32_t x, uint32_t y);
|
||||||
|
|
||||||
// ========== INTERNAL METHODS ==========
|
// ========== INTERNAL METHODS ==========
|
||||||
// (In most use cases you won't need these)
|
// (In most use cases you won't need these)
|
||||||
void register_touch_component(NextionComponentBase *obj) { this->touch_.push_back(obj); }
|
void register_touch_component(NextionComponentBase *obj) { this->touch_.push_back(obj); }
|
||||||
|
|
|
@ -148,7 +148,25 @@ void Nextion::set_component_pic(const char *component, uint8_t pic_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nextion::set_component_picc(const char *component, uint8_t pic_id) {
|
void Nextion::set_component_picc(const char *component, uint8_t pic_id) {
|
||||||
this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.picc=%" PRIu8, component, pic_id);
|
this->add_no_result_to_queue_with_printf_("set_component_picc", "%s.picc=%" PRIu8, component, pic_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set video
|
||||||
|
void Nextion::set_component_vid(const char *component, uint8_t vid_id) {
|
||||||
|
this->add_no_result_to_queue_with_printf_("set_component_vid", "%s.vid=%" PRIu8, component, vid_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Nextion::set_component_drag(const char *component, bool drag) {
|
||||||
|
this->add_no_result_to_queue_with_printf_("set_component_drag", "%s.drag=%i", component, drag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Nextion::set_component_aph(const char *component, uint8_t aph) {
|
||||||
|
this->add_no_result_to_queue_with_printf_("set_component_aph", "%s.aph=%" PRIu8, component, aph);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Nextion::set_component_position(const char *component, uint32_t x, uint32_t y) {
|
||||||
|
this->add_no_result_to_queue_with_printf_("set_component_position_x", "%s.x=%" PRIu32, component, x);
|
||||||
|
this->add_no_result_to_queue_with_printf_("set_component_position_y", "%s.y=%" PRIu32, component, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nextion::set_component_text_printf(const char *component, const char *format, ...) {
|
void Nextion::set_component_text_printf(const char *component, const char *format, ...) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ lib_deps =
|
||||||
bblanchon/ArduinoJson@6.18.5 ; json
|
bblanchon/ArduinoJson@6.18.5 ; json
|
||||||
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
||||||
functionpointer/arduino-MLX90393@1.0.0 ; mlx90393
|
functionpointer/arduino-MLX90393@1.0.0 ; mlx90393
|
||||||
pavlodn/HaierProtocol@0.9.25 ; haier
|
pavlodn/HaierProtocol@0.9.28 ; haier
|
||||||
; This is using the repository until a new release is published to PlatformIO
|
; This is using the repository until a new release is published to PlatformIO
|
||||||
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
|
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
|
||||||
build_flags =
|
build_flags =
|
||||||
|
|
|
@ -93,3 +93,21 @@ binary_sensor:
|
||||||
name: Haier Indoor Fan Status
|
name: Haier Indoor Fan Status
|
||||||
outdoor_fan_status:
|
outdoor_fan_status:
|
||||||
name: Haier Outdoor Fan Status
|
name: Haier Outdoor Fan Status
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
self_cleaning:
|
||||||
|
name: Haier start self cleaning
|
||||||
|
steri_cleaning:
|
||||||
|
name: Haier start 56°C steri-cleaning
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
appliance_name:
|
||||||
|
name: Haier appliance name
|
||||||
|
cleaning_status:
|
||||||
|
name: Haier cleaning status
|
||||||
|
protocol_version:
|
||||||
|
name: Haier protocol version
|
||||||
|
|
|
@ -93,3 +93,21 @@ binary_sensor:
|
||||||
name: Haier Indoor Fan Status
|
name: Haier Indoor Fan Status
|
||||||
outdoor_fan_status:
|
outdoor_fan_status:
|
||||||
name: Haier Outdoor Fan Status
|
name: Haier Outdoor Fan Status
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
self_cleaning:
|
||||||
|
name: Haier start self cleaning
|
||||||
|
steri_cleaning:
|
||||||
|
name: Haier start 56°C steri-cleaning
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
appliance_name:
|
||||||
|
name: Haier appliance name
|
||||||
|
cleaning_status:
|
||||||
|
name: Haier cleaning status
|
||||||
|
protocol_version:
|
||||||
|
name: Haier protocol version
|
||||||
|
|
|
@ -93,3 +93,21 @@ binary_sensor:
|
||||||
name: Haier Indoor Fan Status
|
name: Haier Indoor Fan Status
|
||||||
outdoor_fan_status:
|
outdoor_fan_status:
|
||||||
name: Haier Outdoor Fan Status
|
name: Haier Outdoor Fan Status
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
self_cleaning:
|
||||||
|
name: Haier start self cleaning
|
||||||
|
steri_cleaning:
|
||||||
|
name: Haier start 56°C steri-cleaning
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
appliance_name:
|
||||||
|
name: Haier appliance name
|
||||||
|
cleaning_status:
|
||||||
|
name: Haier cleaning status
|
||||||
|
protocol_version:
|
||||||
|
name: Haier protocol version
|
||||||
|
|
|
@ -93,3 +93,21 @@ binary_sensor:
|
||||||
name: Haier Indoor Fan Status
|
name: Haier Indoor Fan Status
|
||||||
outdoor_fan_status:
|
outdoor_fan_status:
|
||||||
name: Haier Outdoor Fan Status
|
name: Haier Outdoor Fan Status
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
self_cleaning:
|
||||||
|
name: Haier start self cleaning
|
||||||
|
steri_cleaning:
|
||||||
|
name: Haier start 56°C steri-cleaning
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
appliance_name:
|
||||||
|
name: Haier appliance name
|
||||||
|
cleaning_status:
|
||||||
|
name: Haier cleaning status
|
||||||
|
protocol_version:
|
||||||
|
name: Haier protocol version
|
||||||
|
|
|
@ -93,3 +93,21 @@ binary_sensor:
|
||||||
name: Haier Indoor Fan Status
|
name: Haier Indoor Fan Status
|
||||||
outdoor_fan_status:
|
outdoor_fan_status:
|
||||||
name: Haier Outdoor Fan Status
|
name: Haier Outdoor Fan Status
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
self_cleaning:
|
||||||
|
name: Haier start self cleaning
|
||||||
|
steri_cleaning:
|
||||||
|
name: Haier start 56°C steri-cleaning
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
appliance_name:
|
||||||
|
name: Haier appliance name
|
||||||
|
cleaning_status:
|
||||||
|
name: Haier cleaning status
|
||||||
|
protocol_version:
|
||||||
|
name: Haier protocol version
|
||||||
|
|
|
@ -93,3 +93,21 @@ binary_sensor:
|
||||||
name: Haier Indoor Fan Status
|
name: Haier Indoor Fan Status
|
||||||
outdoor_fan_status:
|
outdoor_fan_status:
|
||||||
name: Haier Outdoor Fan Status
|
name: Haier Outdoor Fan Status
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
self_cleaning:
|
||||||
|
name: Haier start self cleaning
|
||||||
|
steri_cleaning:
|
||||||
|
name: Haier start 56°C steri-cleaning
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
appliance_name:
|
||||||
|
name: Haier appliance name
|
||||||
|
cleaning_status:
|
||||||
|
name: Haier cleaning status
|
||||||
|
protocol_version:
|
||||||
|
name: Haier protocol version
|
||||||
|
|
41
tests/components/mpr121/common.yaml
Normal file
41
tests/components/mpr121/common.yaml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
i2c:
|
||||||
|
- id: i2c_mpr121
|
||||||
|
scl: ${i2c_scl}
|
||||||
|
sda: ${i2c_sda}
|
||||||
|
|
||||||
|
mpr121:
|
||||||
|
id: mpr121_first
|
||||||
|
address: 0x5A
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: mpr121
|
||||||
|
id: touchkey0
|
||||||
|
name: touchkey0
|
||||||
|
channel: 0
|
||||||
|
- platform: mpr121
|
||||||
|
id: bin1
|
||||||
|
name: touchkey1
|
||||||
|
channel: 1
|
||||||
|
- platform: mpr121
|
||||||
|
id: bin2
|
||||||
|
name: touchkey2
|
||||||
|
channel: 2
|
||||||
|
- platform: mpr121
|
||||||
|
id: bin3
|
||||||
|
name: touchkey3
|
||||||
|
channel: 6
|
||||||
|
|
||||||
|
output:
|
||||||
|
- platform: gpio
|
||||||
|
id: gpio1
|
||||||
|
pin:
|
||||||
|
mpr121: mpr121_first
|
||||||
|
number: 7
|
||||||
|
mode: OUTPUT
|
||||||
|
- platform: gpio
|
||||||
|
id: gpio2
|
||||||
|
pin:
|
||||||
|
mpr121: mpr121_first
|
||||||
|
number: 11
|
||||||
|
mode: OUTPUT
|
||||||
|
inverted: true
|
|
@ -1,26 +1,5 @@
|
||||||
i2c:
|
substitutions:
|
||||||
- id: i2c_mpr121
|
i2c_scl: GPIO5
|
||||||
scl: 5
|
i2c_sda: GPIO4
|
||||||
sda: 4
|
|
||||||
|
|
||||||
mpr121:
|
<<: !include common.yaml
|
||||||
id: mpr121_first
|
|
||||||
address: 0x5A
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: mpr121
|
|
||||||
id: touchkey0
|
|
||||||
name: touchkey0
|
|
||||||
channel: 0
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin1
|
|
||||||
name: touchkey1
|
|
||||||
channel: 1
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin2
|
|
||||||
name: touchkey2
|
|
||||||
channel: 2
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin3
|
|
||||||
name: touchkey3
|
|
||||||
channel: 3
|
|
||||||
|
|
|
@ -1,26 +1,5 @@
|
||||||
i2c:
|
substitutions:
|
||||||
- id: i2c_mpr121
|
i2c_scl: GPIO5
|
||||||
scl: 5
|
i2c_sda: GPIO4
|
||||||
sda: 4
|
|
||||||
|
|
||||||
mpr121:
|
<<: !include common.yaml
|
||||||
id: mpr121_first
|
|
||||||
address: 0x5A
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: mpr121
|
|
||||||
id: touchkey0
|
|
||||||
name: touchkey0
|
|
||||||
channel: 0
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin1
|
|
||||||
name: touchkey1
|
|
||||||
channel: 1
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin2
|
|
||||||
name: touchkey2
|
|
||||||
channel: 2
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin3
|
|
||||||
name: touchkey3
|
|
||||||
channel: 3
|
|
||||||
|
|
|
@ -1,26 +1,5 @@
|
||||||
i2c:
|
substitutions:
|
||||||
- id: i2c_mpr121
|
i2c_scl: GPIO16
|
||||||
scl: 16
|
i2c_sda: GPIO17
|
||||||
sda: 17
|
|
||||||
|
|
||||||
mpr121:
|
<<: !include common.yaml
|
||||||
id: mpr121_first
|
|
||||||
address: 0x5A
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: mpr121
|
|
||||||
id: touchkey0
|
|
||||||
name: touchkey0
|
|
||||||
channel: 0
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin1
|
|
||||||
name: touchkey1
|
|
||||||
channel: 1
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin2
|
|
||||||
name: touchkey2
|
|
||||||
channel: 2
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin3
|
|
||||||
name: touchkey3
|
|
||||||
channel: 3
|
|
||||||
|
|
|
@ -1,26 +1,5 @@
|
||||||
i2c:
|
substitutions:
|
||||||
- id: i2c_mpr121
|
i2c_scl: GPIO16
|
||||||
scl: 16
|
i2c_sda: GPIO17
|
||||||
sda: 17
|
|
||||||
|
|
||||||
mpr121:
|
<<: !include common.yaml
|
||||||
id: mpr121_first
|
|
||||||
address: 0x5A
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: mpr121
|
|
||||||
id: touchkey0
|
|
||||||
name: touchkey0
|
|
||||||
channel: 0
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin1
|
|
||||||
name: touchkey1
|
|
||||||
channel: 1
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin2
|
|
||||||
name: touchkey2
|
|
||||||
channel: 2
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin3
|
|
||||||
name: touchkey3
|
|
||||||
channel: 3
|
|
||||||
|
|
|
@ -1,26 +1,5 @@
|
||||||
i2c:
|
substitutions:
|
||||||
- id: i2c_mpr121
|
i2c_scl: GPIO5
|
||||||
scl: 5
|
i2c_sda: GPIO4
|
||||||
sda: 4
|
|
||||||
|
|
||||||
mpr121:
|
<<: !include common.yaml
|
||||||
id: mpr121_first
|
|
||||||
address: 0x5A
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: mpr121
|
|
||||||
id: touchkey0
|
|
||||||
name: touchkey0
|
|
||||||
channel: 0
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin1
|
|
||||||
name: touchkey1
|
|
||||||
channel: 1
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin2
|
|
||||||
name: touchkey2
|
|
||||||
channel: 2
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin3
|
|
||||||
name: touchkey3
|
|
||||||
channel: 3
|
|
||||||
|
|
|
@ -1,26 +1,5 @@
|
||||||
i2c:
|
substitutions:
|
||||||
- id: i2c_mpr121
|
i2c_scl: GPIO5
|
||||||
scl: 5
|
i2c_sda: GPIO4
|
||||||
sda: 4
|
|
||||||
|
|
||||||
mpr121:
|
<<: !include common.yaml
|
||||||
id: mpr121_first
|
|
||||||
address: 0x5A
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: mpr121
|
|
||||||
id: touchkey0
|
|
||||||
name: touchkey0
|
|
||||||
channel: 0
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin1
|
|
||||||
name: touchkey1
|
|
||||||
channel: 1
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin2
|
|
||||||
name: touchkey2
|
|
||||||
channel: 2
|
|
||||||
- platform: mpr121
|
|
||||||
id: bin3
|
|
||||||
name: touchkey3
|
|
||||||
channel: 3
|
|
||||||
|
|
|
@ -344,10 +344,6 @@ apds9960:
|
||||||
address: 0x20
|
address: 0x20
|
||||||
update_interval: 60s
|
update_interval: 60s
|
||||||
|
|
||||||
mpr121:
|
|
||||||
id: mpr121_first
|
|
||||||
address: 0x5A
|
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: apds9960
|
- platform: apds9960
|
||||||
direction: up
|
direction: up
|
||||||
|
@ -371,25 +367,6 @@ binary_sensor:
|
||||||
direction: right
|
direction: right
|
||||||
name: APDS9960 Right
|
name: APDS9960 Right
|
||||||
|
|
||||||
- platform: mpr121
|
|
||||||
id: touchkey0
|
|
||||||
channel: 0
|
|
||||||
name: touchkey0
|
|
||||||
- platform: mpr121
|
|
||||||
channel: 1
|
|
||||||
name: touchkey1
|
|
||||||
id: bin1
|
|
||||||
- platform: mpr121
|
|
||||||
channel: 2
|
|
||||||
name: touchkey2
|
|
||||||
id: bin2
|
|
||||||
- platform: mpr121
|
|
||||||
channel: 3
|
|
||||||
name: touchkey3
|
|
||||||
id: bin3
|
|
||||||
on_press:
|
|
||||||
then:
|
|
||||||
- switch.toggle: mpr121_toggle
|
|
||||||
- platform: ttp229_lsf
|
- platform: ttp229_lsf
|
||||||
channel: 1
|
channel: 1
|
||||||
name: TTP229 LSF Test
|
name: TTP229 LSF Test
|
||||||
|
@ -443,10 +420,6 @@ grove_tb6612fng:
|
||||||
address: 0x14
|
address: 0x14
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: template
|
|
||||||
name: mpr121_toggle
|
|
||||||
id: mpr121_toggle
|
|
||||||
optimistic: true
|
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
id: gpio_switch1
|
id: gpio_switch1
|
||||||
pin:
|
pin:
|
||||||
|
|
Loading…
Reference in a new issue