mirror of
https://github.com/esphome/esphome.git
synced 2025-03-14 06:38:17 +00:00
Add vertical_direction select comp. for platform airton
A select component called vertical_direction will be available as a config for the airton climate component; additionally, now changing the display or sleep switches will immediately send the IR command.
This commit is contained in:
parent
3d324e3534
commit
236f02f573
@ -6,30 +6,62 @@ namespace airton {
|
||||
|
||||
static const char *const TAG = "airton.climate";
|
||||
|
||||
void AirtonClimate::set_sleep_mode_state(bool state) {
|
||||
void AirtonClimate::set_sleep_mode_state(bool state, bool send_ir = false) {
|
||||
if (state != this->settings_.sleep_state) {
|
||||
this->settings_.sleep_state = state;
|
||||
#ifdef USE_SWITCH
|
||||
this->sleep_mode_switch_->publish_state(state);
|
||||
#endif
|
||||
this->airton_rtc_.save(&this->settings_);
|
||||
if (send_ir)
|
||||
this->transmit_state();
|
||||
}
|
||||
}
|
||||
|
||||
bool AirtonClimate::get_sleep_mode_state() const { return this->settings_.sleep_state; }
|
||||
|
||||
void AirtonClimate::set_display_state(bool state) {
|
||||
void AirtonClimate::set_display_state(bool state, bool send_ir = false) {
|
||||
if (state != this->settings_.display_state) {
|
||||
this->settings_.display_state = state;
|
||||
#ifdef USE_SWITCH
|
||||
this->display_switch_->publish_state(state);
|
||||
#endif
|
||||
this->airton_rtc_.save(&this->settings_);
|
||||
if (send_ir)
|
||||
this->transmit_state();
|
||||
}
|
||||
}
|
||||
|
||||
bool AirtonClimate::get_display_state() const { return this->settings_.display_state; }
|
||||
|
||||
void AirtonClimate::set_vertical_direction_state(VerticalDirection state) {
|
||||
if (state.toUint8() != this->settings_.vertical_direction_state.toUint8()) {
|
||||
this->settings_.vertical_direction_state = state;
|
||||
#ifdef USE_SELECT
|
||||
this->vertical_direction_select_->publish_state(state.toString());
|
||||
#endif
|
||||
this->airton_rtc_.save(&this->settings_);
|
||||
}
|
||||
}
|
||||
void AirtonClimate::set_vertical_direction_state(const std::string state) {
|
||||
if (state != this->settings_.vertical_direction_state.toString()) {
|
||||
VerticalDirection new_state;
|
||||
new_state.setDirection(state);
|
||||
this->settings_.vertical_direction_state = new_state.getDirection();
|
||||
#ifdef USE_SELECT
|
||||
this->vertical_direction_select_->publish_state(state);
|
||||
#endif
|
||||
this->airton_rtc_.save(&this->settings_);
|
||||
// This overloaded function is called only from the select component, upon changing selection
|
||||
// Therefore, we transmit the updated state after saving it
|
||||
this->transmit_state();
|
||||
}
|
||||
}
|
||||
|
||||
VerticalDirection AirtonClimate::get_vertical_direction_state() const {
|
||||
return this->settings_.vertical_direction_state;
|
||||
}
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
void AirtonClimate::set_sleep_mode_switch(switch_::Switch *sw) {
|
||||
this->sleep_mode_switch_ = sw;
|
||||
@ -45,6 +77,15 @@ void AirtonClimate::set_display_switch(switch_::Switch *sw) {
|
||||
}
|
||||
#endif // USE_SWITCH
|
||||
|
||||
#ifdef USE_SELECT
|
||||
void AirtonClimate::set_vertical_direction_select(select::Select *sel) {
|
||||
this->vertical_direction_select_ = sel;
|
||||
if (this->vertical_direction_select_ != nullptr) {
|
||||
this->vertical_direction_select_->publish_state(this->get_vertical_direction_state().toString());
|
||||
}
|
||||
}
|
||||
#endif // USE_SELECT
|
||||
|
||||
uint8_t AirtonClimate::get_previous_mode_() { return previous_mode_; }
|
||||
|
||||
void AirtonClimate::set_previous_mode_(uint8_t mode) { previous_mode_ = mode; }
|
||||
@ -68,7 +109,7 @@ void AirtonClimate::transmit_state() {
|
||||
remote_state[3] |= this->temperature_();
|
||||
|
||||
remote_state[4] = 0;
|
||||
remote_state[4] |= this->swing_mode_();
|
||||
remote_state[4] |= this->get_vertical_direction_state().toUint8();
|
||||
|
||||
remote_state[5] = this->operation_settings_();
|
||||
|
||||
@ -165,18 +206,6 @@ uint8_t AirtonClimate::temperature_() {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t AirtonClimate::swing_mode_() {
|
||||
uint8_t swing_control = 0b01100000;
|
||||
switch (this->swing_mode) {
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
swing_control |= 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return swing_control;
|
||||
}
|
||||
|
||||
// The bits of this packet's byte have the following meanings (from MSB to LSB)
|
||||
// Light, Health, Unknown, HeatOn, Unknown, NotAutoOn, Sleep, Econo
|
||||
uint8_t AirtonClimate::operation_settings_() {
|
||||
@ -231,19 +260,8 @@ bool AirtonClimate::parse_state_frame_(uint8_t const frame[]) {
|
||||
} else {
|
||||
this->mode = climate::CLIMATE_MODE_OFF;
|
||||
}
|
||||
uint8_t temperature = frame[3];
|
||||
this->target_temperature =
|
||||
(temperature & 0b00001111) + 16; // Mask the higher half of the byte (unused), add back the offset
|
||||
|
||||
uint8_t fan_mode = (frame[2] & 0b01110000) >> 4; // Mask anything but bits 5-7, then shift them to the right
|
||||
|
||||
uint8_t swing_mode = frame[4] & 0b00000001; // Mask anything but the LSB
|
||||
if (swing_mode) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||
} else {
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
|
||||
switch (fan_mode) {
|
||||
case AIRTON_FAN_1:
|
||||
case AIRTON_FAN_2:
|
||||
@ -261,6 +279,18 @@ bool AirtonClimate::parse_state_frame_(uint8_t const frame[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t temperature = frame[3];
|
||||
this->target_temperature =
|
||||
(temperature & 0b00001111) + 16; // Mask the higher half of the byte (unused), add back the offset
|
||||
|
||||
uint8_t swing_mode = frame[4] & 0b00001111; // Mask the higher nibble
|
||||
if (swing_mode == (uint8_t) VerticalDirection::VERTICAL_DIRECTION_OFF) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
} else {
|
||||
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||
}
|
||||
this->set_vertical_direction_state(static_cast<VerticalDirection::Direction>(swing_mode));
|
||||
|
||||
uint8_t display_light = frame[5] & 0b10000000; // Mask anything but the MSB
|
||||
this->set_display_state(display_light != 0);
|
||||
|
||||
|
@ -1,11 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/climate_ir/climate_ir.h"
|
||||
#include "quirks.h"
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
#include "esphome/components/select/select.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace airton {
|
||||
|
||||
@ -45,8 +50,18 @@ const uint8_t AIRTON_STATE_FRAME_SIZE = 7;
|
||||
struct AirtonSettings {
|
||||
bool sleep_state;
|
||||
bool display_state;
|
||||
VerticalDirection vertical_direction_state;
|
||||
};
|
||||
|
||||
// Local vertical direction constants
|
||||
static const VerticalDirection VERTICAL_DIRECTION_OFF = VerticalDirection::VERTICAL_DIRECTION_OFF;
|
||||
static const VerticalDirection VERTICAL_DIRECTION_SWING = VerticalDirection::VERTICAL_DIRECTION_SWING;
|
||||
static const VerticalDirection VERTICAL_DIRECTION_UP = VerticalDirection::VERTICAL_DIRECTION_UP;
|
||||
static const VerticalDirection VERTICAL_DIRECTION_MIDDLE_UP = VerticalDirection::VERTICAL_DIRECTION_MIDDLE_UP;
|
||||
static const VerticalDirection VERTICAL_DIRECTION_MIDDLE = VerticalDirection::VERTICAL_DIRECTION_MIDDLE;
|
||||
static const VerticalDirection VERTICAL_DIRECTION_MIDDLE_DOWN = VerticalDirection::VERTICAL_DIRECTION_MIDDLE_DOWN;
|
||||
static const VerticalDirection VERTICAL_DIRECTION_DOWN = VerticalDirection::VERTICAL_DIRECTION_DOWN;
|
||||
|
||||
class AirtonClimate : public climate_ir::ClimateIR {
|
||||
#ifdef USE_SWITCH
|
||||
public:
|
||||
@ -56,6 +71,13 @@ class AirtonClimate : public climate_ir::ClimateIR {
|
||||
protected:
|
||||
switch_::Switch *sleep_mode_switch_{nullptr};
|
||||
switch_::Switch *display_switch_{nullptr};
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
public:
|
||||
void set_vertical_direction_select(select::Select *sel);
|
||||
|
||||
protected:
|
||||
select::Select *vertical_direction_select_{nullptr};
|
||||
#endif
|
||||
public:
|
||||
AirtonClimate()
|
||||
@ -63,10 +85,13 @@ class AirtonClimate : public climate_ir::ClimateIR {
|
||||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||
climate::CLIMATE_FAN_HIGH},
|
||||
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
|
||||
void set_sleep_mode_state(bool state);
|
||||
void set_sleep_mode_state(bool state, bool send_ir);
|
||||
bool get_sleep_mode_state() const;
|
||||
void set_display_state(bool state);
|
||||
void set_display_state(bool state, bool send_ir);
|
||||
bool get_display_state() const;
|
||||
void set_vertical_direction_state(VerticalDirection state);
|
||||
void set_vertical_direction_state(std::string state);
|
||||
VerticalDirection get_vertical_direction_state() const;
|
||||
|
||||
private:
|
||||
// Save the previous operation mode inside instance
|
||||
@ -85,7 +110,6 @@ class AirtonClimate : public climate_ir::ClimateIR {
|
||||
uint16_t fan_speed_();
|
||||
bool turbo_control_();
|
||||
uint8_t temperature_();
|
||||
uint8_t swing_mode_();
|
||||
uint8_t operation_settings_();
|
||||
|
||||
uint8_t sum_bytes_(const uint8_t *start, uint16_t length);
|
||||
|
@ -11,10 +11,25 @@ AirtonClimate = airton_ns.class_("AirtonClimate", climate_ir.ClimateIR)
|
||||
|
||||
CONF_AIRTON_ID = "airton_id"
|
||||
CONF_SLEEP_MODE = "sleep_mode"
|
||||
CONF_VERTICAL_DIRECTION = "vertical_direction"
|
||||
|
||||
VerticalDirections = airton_ns.enum("VerticalDirections")
|
||||
VERTICAL_DIRECTIONS = {
|
||||
"off": VerticalDirections.VERTICAL_DIRECTION_OFF,
|
||||
"swing": VerticalDirections.VERTICAL_DIRECTION_SWING,
|
||||
"up": VerticalDirections.VERTICAL_DIRECTION_UP,
|
||||
"middle-up": VerticalDirections.VERTICAL_DIRECTION_MIDDLE_UP,
|
||||
"middle": VerticalDirections.VERTICAL_DIRECTION_MIDDLE,
|
||||
"middle-down": VerticalDirections.VERTICAL_DIRECTION_MIDDLE_DOWN,
|
||||
"down": VerticalDirections.VERTICAL_DIRECTION_DOWN,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AirtonClimate),
|
||||
cv.Optional(CONF_VERTICAL_DIRECTION, default="off"): cv.enum(
|
||||
VERTICAL_DIRECTIONS
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@ -57,3 +72,4 @@ async def sleep_action_to_code(config, action_id, template_arg, args):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await climate_ir.register_climate_ir(var, config)
|
||||
cg.add(var.set_vertical_direction_state(config[CONF_VERTICAL_DIRECTION]))
|
||||
|
76
esphome/components/airton/quirks.h
Normal file
76
esphome/components/airton/quirks.h
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef AIRTON_QUIRKS_H
|
||||
#define AIRTON_QUIRKS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airton {
|
||||
|
||||
// Vertical direction for air outlet flap
|
||||
class VerticalDirection {
|
||||
public:
|
||||
enum Direction {
|
||||
VERTICAL_DIRECTION_OFF = 0x00,
|
||||
VERTICAL_DIRECTION_SWING = 0x01,
|
||||
VERTICAL_DIRECTION_UP = 0x02,
|
||||
VERTICAL_DIRECTION_MIDDLE_UP = 0x03,
|
||||
VERTICAL_DIRECTION_MIDDLE = 0x04,
|
||||
VERTICAL_DIRECTION_MIDDLE_DOWN = 0x05,
|
||||
VERTICAL_DIRECTION_DOWN = 0x06,
|
||||
};
|
||||
|
||||
VerticalDirection(Direction direction = Direction::VERTICAL_DIRECTION_OFF) : _direction(direction) {}
|
||||
|
||||
std::string toString() const {
|
||||
switch (_direction) {
|
||||
case Direction::VERTICAL_DIRECTION_SWING:
|
||||
return "swing";
|
||||
case Direction::VERTICAL_DIRECTION_UP:
|
||||
return "up";
|
||||
case Direction::VERTICAL_DIRECTION_MIDDLE_UP:
|
||||
return "middle-up";
|
||||
case Direction::VERTICAL_DIRECTION_MIDDLE:
|
||||
return "middle";
|
||||
case Direction::VERTICAL_DIRECTION_MIDDLE_DOWN:
|
||||
return "middle-down";
|
||||
case Direction::VERTICAL_DIRECTION_DOWN:
|
||||
return "down";
|
||||
case Direction::VERTICAL_DIRECTION_OFF:
|
||||
default:
|
||||
return "off";
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t toUint8() const { return static_cast<uint8_t>(_direction); }
|
||||
|
||||
void setDirection(Direction direction) { _direction = direction; }
|
||||
void setDirection(std::string direction) {
|
||||
if (direction == "swing") {
|
||||
_direction = Direction::VERTICAL_DIRECTION_SWING;
|
||||
} else if (direction == "up") {
|
||||
_direction = Direction::VERTICAL_DIRECTION_UP;
|
||||
} else if (direction == "middle-up") {
|
||||
_direction = Direction::VERTICAL_DIRECTION_MIDDLE_UP;
|
||||
} else if (direction == "middle") {
|
||||
_direction = Direction::VERTICAL_DIRECTION_MIDDLE;
|
||||
} else if (direction == "middle-down") {
|
||||
_direction = Direction::VERTICAL_DIRECTION_MIDDLE_DOWN;
|
||||
} else if (direction == "down") {
|
||||
_direction = Direction::VERTICAL_DIRECTION_DOWN;
|
||||
} else {
|
||||
_direction = Direction::VERTICAL_DIRECTION_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
Direction getDirection() const { return _direction; }
|
||||
|
||||
private:
|
||||
Direction _direction;
|
||||
};
|
||||
|
||||
} // namespace airton
|
||||
} // namespace esphome
|
||||
|
||||
#endif // AIRTON_QUIRKS_H
|
38
esphome/components/airton/select/__init__.py
Normal file
38
esphome/components/airton/select/__init__.py
Normal file
@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import select
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import ENTITY_CATEGORY_CONFIG
|
||||
|
||||
from ..climate import (
|
||||
CONF_AIRTON_ID,
|
||||
CONF_VERTICAL_DIRECTION,
|
||||
VERTICAL_DIRECTIONS,
|
||||
AirtonClimate,
|
||||
airton_ns,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@procsiab"]
|
||||
|
||||
VerticalDirectionSelect = airton_ns.class_("VerticalDirectionSelect", select.Select)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_AIRTON_ID): cv.use_id(AirtonClimate),
|
||||
cv.Optional(CONF_VERTICAL_DIRECTION): select.select_schema(
|
||||
VerticalDirectionSelect,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_AIRTON_ID])
|
||||
|
||||
for select_type in [CONF_VERTICAL_DIRECTION]:
|
||||
if conf := config.get(select_type):
|
||||
sel_var = await select.new_select(
|
||||
conf, options=list(VERTICAL_DIRECTIONS.keys())
|
||||
)
|
||||
await cg.register_parented(sel_var, parent)
|
||||
cg.add(getattr(parent, f"set_{select_type}_select")(sel_var))
|
15
esphome/components/airton/select/direction.cpp
Normal file
15
esphome/components/airton/select/direction.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "direction.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airton {
|
||||
|
||||
void VerticalDirectionSelect::control(const std::string &value) {
|
||||
if (this->parent_->get_vertical_direction_state().toString() != value) {
|
||||
ESP_LOGD("vertical_direction", "Select received value: %s", value.c_str());
|
||||
this->parent_->set_vertical_direction_state(value);
|
||||
}
|
||||
this->publish_state(value);
|
||||
}
|
||||
|
||||
} // namespace airton
|
||||
} // namespace esphome
|
18
esphome/components/airton/select/direction.h
Normal file
18
esphome/components/airton/select/direction.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "../airton.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airton {
|
||||
|
||||
class VerticalDirectionSelect : public select::Select, public Parented<AirtonClimate> {
|
||||
public:
|
||||
VerticalDirectionSelect() = default;
|
||||
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
};
|
||||
|
||||
} // namespace airton
|
||||
} // namespace esphome
|
@ -5,7 +5,7 @@ namespace airton {
|
||||
|
||||
void DisplaySwitch::write_state(bool state) {
|
||||
if (this->parent_->get_display_state() != state) {
|
||||
this->parent_->set_display_state(state);
|
||||
this->parent_->set_display_state(state, true);
|
||||
}
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace airton {
|
||||
|
||||
void SleepSwitch::write_state(bool state) {
|
||||
if (this->parent_->get_sleep_mode_state() != state) {
|
||||
this->parent_->set_sleep_mode_state(state);
|
||||
this->parent_->set_sleep_mode_state(state, true);
|
||||
}
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user