Add switches for sleep mode and light, update tests

This commit is contained in:
Lorenzo Prosseda 2024-11-14 19:28:03 +01:00
parent 83df28fc9a
commit 05e87edcd4
No known key found for this signature in database
GPG key ID: 316B7756E0101C16
13 changed files with 253 additions and 2 deletions

View file

@ -6,6 +6,45 @@ namespace airton {
static const char *const TAG = "airton.climate"; static const char *const TAG = "airton.climate";
void AirtonClimate::set_sleep_state(bool state) {
if (state != this->settings_.sleep_state) {
this->settings_.sleep_state = state;
#ifdef USE_SWITCH
this->sleep_switch_->publish_state(state);
#endif
this->airton_rtc_.save(&this->settings_);
}
}
bool AirtonClimate::get_sleep_state() const { return this->settings_.sleep_state; }
void AirtonClimate::set_display_state(bool state) {
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_);
}
}
bool AirtonClimate::get_display_state() const { return this->settings_.display_state; }
#ifdef USE_SWITCH
void AirtonClimate::set_sleep_switch(switch_::Switch *sw) {
this->sleep_switch_ = sw;
if (this->sleep_switch_ != nullptr) {
this->sleep_switch_->publish_state(this->get_sleep_state());
}
}
void AirtonClimate::set_display_switch(switch_::Switch *sw) {
this->display_switch_ = sw;
if (this->display_switch_ != nullptr) {
this->display_switch_->publish_state(this->get_display_state());
}
}
#endif // USE_SWITCH
uint8_t AirtonClimate::get_previous_mode_() { return previous_mode_; } uint8_t AirtonClimate::get_previous_mode_() { return previous_mode_; }
void AirtonClimate::set_previous_mode_(uint8_t mode) { previous_mode_ = mode; } void AirtonClimate::set_previous_mode_(uint8_t mode) { previous_mode_ = mode; }
@ -115,7 +154,6 @@ bool AirtonClimate::turbo_control_() {
} }
uint8_t AirtonClimate::temperature_() { uint8_t AirtonClimate::temperature_() {
// Force 20C degrees in Fan only mode
switch (this->mode) { switch (this->mode) {
case climate::CLIMATE_MODE_HEAT_COOL: case climate::CLIMATE_MODE_HEAT_COOL:
// Fixed 25C setpoint in Auto mode // Fixed 25C setpoint in Auto mode
@ -146,7 +184,13 @@ uint8_t AirtonClimate::operation_settings_() {
if (this->mode == climate::CLIMATE_MODE_HEAT) { // Set heating bit if on the corresponding mode if (this->mode == climate::CLIMATE_MODE_HEAT) { // Set heating bit if on the corresponding mode
settings |= (1 << 4); settings |= (1 << 4);
} }
settings |= 0b11000100; // Set Light, Health and NotAutoOn bits as per default if (this->get_display_state()) { // Set LED display
settings |= (1 << 7);
}
if (this->get_sleep_state()) { // Set sleep mode
settings |= (1 << 1);
}
settings |= 0b01000100; // Set Health and NotAutoOn bits as per default
return settings; return settings;
} }
@ -217,6 +261,12 @@ bool AirtonClimate::parse_state_frame_(uint8_t const frame[]) {
break; break;
} }
uint8_t display_light = frame[5] & 0b10000000; // Mask anything but the MSB
this->set_display_state(display_light != 0);
uint8_t sleep_mode = frame[5] & 0b00000010; // Mask anything but the second bit
this->set_sleep_state(sleep_mode != 0);
this->publish_state(); this->publish_state();
return true; return true;
} }

View file

@ -2,6 +2,10 @@
#include "esphome/components/climate_ir/climate_ir.h" #include "esphome/components/climate_ir/climate_ir.h"
#ifdef USE_SWITCH
#include "esphome/components/switch/switch.h"
#endif
namespace esphome { namespace esphome {
namespace airton { namespace airton {
@ -37,13 +41,32 @@ const uint32_t AIRTON_MESSAGE_SPACE = 100000;
// State Frame size // State Frame size
const uint8_t AIRTON_STATE_FRAME_SIZE = 7; const uint8_t AIRTON_STATE_FRAME_SIZE = 7;
// Specific internal unit settings
struct AirtonSettings {
bool sleep_state;
bool display_state;
};
class AirtonClimate : public climate_ir::ClimateIR { class AirtonClimate : public climate_ir::ClimateIR {
#ifdef USE_SWITCH
public:
void set_sleep_switch(switch_::Switch *sw);
void set_display_switch(switch_::Switch *sw);
protected:
switch_::Switch *sleep_switch_{nullptr};
switch_::Switch *display_switch_{nullptr};
#endif
public: public:
AirtonClimate() AirtonClimate()
: climate_ir::ClimateIR(AIRTON_TEMP_MIN, AIRTON_TEMP_MAX, 1.0f, true, true, : climate_ir::ClimateIR(AIRTON_TEMP_MIN, AIRTON_TEMP_MAX, 1.0f, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH}, climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {} {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
void set_sleep_state(bool state);
bool get_sleep_state() const;
void set_display_state(bool state);
bool get_display_state() const;
private: private:
// Save the previous operation mode inside instance // Save the previous operation mode inside instance
@ -52,6 +75,8 @@ class AirtonClimate : public climate_ir::ClimateIR {
protected: protected:
uint8_t get_previous_mode_(); uint8_t get_previous_mode_();
void set_previous_mode_(uint8_t mode); void set_previous_mode_(uint8_t mode);
AirtonSettings settings_;
ESPPreferenceObject airton_rtc_;
// IR transmission payload builder // IR transmission payload builder
void transmit_state() override; void transmit_state() override;

View file

@ -1,3 +1,4 @@
from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import climate_ir from esphome.components import climate_ir
import esphome.config_validation as cv import esphome.config_validation as cv
@ -8,12 +9,49 @@ AUTO_LOAD = ["climate_ir"]
airton_ns = cg.esphome_ns.namespace("airton") airton_ns = cg.esphome_ns.namespace("airton")
AirtonClimate = airton_ns.class_("AirtonClimate", climate_ir.ClimateIR) AirtonClimate = airton_ns.class_("AirtonClimate", climate_ir.ClimateIR)
CONF_AIRTON_ID = "airton_id"
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{ {
cv.GenerateID(): cv.declare_id(AirtonClimate), cv.GenerateID(): cv.declare_id(AirtonClimate),
} }
) )
DisplayOnAction = airton_ns.class_("DisplayOnAction", automation.Action)
DisplayOffAction = airton_ns.class_("DisplayOffAction", automation.Action)
SleepOnAction = airton_ns.class_("SleepOnAction", automation.Action)
SleepOffAction = airton_ns.class_("SleepOffAction", automation.Action)
AIRTON_ACTION_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(AirtonClimate),
}
)
@automation.register_action(
"climate_ir.airton.display_on", DisplayOnAction, AIRTON_ACTION_SCHEMA
)
@automation.register_action(
"climate_ir.airton.display_off", DisplayOffAction, AIRTON_ACTION_SCHEMA
)
async def display_action_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(
"climate_ir.airton.sleep_on", SleepOnAction, AIRTON_ACTION_SCHEMA
)
@automation.register_action(
"climate_ir.airton.sleep_off", SleepOffAction, AIRTON_ACTION_SCHEMA
)
async def sleep_action_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
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])

View file

@ -0,0 +1,39 @@
import esphome.codegen as cg
from esphome.components import switch
import esphome.config_validation as cv
from esphome.const import CONF_DISPLAY, ENTITY_CATEGORY_CONFIG
from ..climate import CONF_AIRTON_ID, AirtonClimate, airton_ns
CODEOWNERS = ["@procsiab"]
CONF_SLEEP_MODE = "sleep"
SleepSwitch = airton_ns.class_("SleepSwitch", switch.Switch)
DisplaySwitch = airton_ns.class_("DisplaySwitch", switch.Switch)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_AIRTON_ID): cv.use_id(AirtonClimate),
cv.Optional(CONF_DISPLAY): switch.switch_schema(
DisplaySwitch,
entity_category=ENTITY_CATEGORY_CONFIG,
default_restore_mode="DISABLED",
),
cv.Optional(CONF_SLEEP_MODE): switch.switch_schema(
SleepSwitch,
entity_category=ENTITY_CATEGORY_CONFIG,
default_restore_mode="DISABLED",
),
}
)
async def to_code(config):
parent = await cg.get_variable(config[CONF_AIRTON_ID])
for switch_type in [CONF_DISPLAY, CONF_SLEEP_MODE]:
if conf := config.get(switch_type):
sw_var = await switch.new_switch(conf)
await cg.register_parented(sw_var, parent)
cg.add(getattr(parent, f"set_{switch_type}_switch")(sw_var))

View file

@ -0,0 +1,14 @@
#include "display.h"
namespace esphome {
namespace airton {
void DisplaySwitch::write_state(bool state) {
if (this->parent_->get_display_state() != state) {
this->parent_->set_display_state(state);
}
this->publish_state(state);
}
} // namespace airton
} // namespace esphome

View file

@ -0,0 +1,18 @@
#pragma once
#include "esphome/components/switch/switch.h"
#include "../airton.h"
namespace esphome {
namespace airton {
class DisplaySwitch : public switch_::Switch, public Parented<AirtonClimate> {
public:
DisplaySwitch() = default;
protected:
void write_state(bool state) override;
};
} // namespace airton
} // namespace esphome

View file

@ -0,0 +1,14 @@
#include "sleep.h"
namespace esphome {
namespace airton {
void SleepSwitch::write_state(bool state) {
if (this->parent_->get_sleep_state() != state) {
this->parent_->set_sleep_state(state);
}
this->publish_state(state);
}
} // namespace airton
} // namespace esphome

View file

@ -0,0 +1,18 @@
#pragma once
#include "esphome/components/switch/switch.h"
#include "../airton.h"
namespace esphome {
namespace airton {
class SleepSwitch : public switch_::Switch, public Parented<AirtonClimate> {
public:
SleepSwitch() = default;
protected:
void write_state(bool state) override;
};
} // namespace airton
} // namespace esphome

View file

@ -23,3 +23,10 @@ sensor:
- platform: template - platform: template
id: airton_sensor id: airton_sensor
lambda: "return 21;" lambda: "return 21;"
switch:
- platform: airton
sleep:
name: "sleep mode"
display:
name: "display light"

View file

@ -23,3 +23,10 @@ sensor:
- platform: template - platform: template
id: airton_sensor id: airton_sensor
lambda: "return 21;" lambda: "return 21;"
switch:
- platform: airton
sleep:
name: "sleep mode"
display:
name: "display light"

View file

@ -23,3 +23,10 @@ sensor:
- platform: template - platform: template
id: airton_sensor id: airton_sensor
lambda: "return 21;" lambda: "return 21;"
switch:
- platform: airton
sleep:
name: "sleep mode"
display:
name: "display light"

View file

@ -23,3 +23,10 @@ sensor:
- platform: template - platform: template
id: airton_sensor id: airton_sensor
lambda: "return 21;" lambda: "return 21;"
switch:
- platform: airton
sleep:
name: "sleep mode"
display:
name: "display light"

View file

@ -23,3 +23,10 @@ sensor:
- platform: template - platform: template
id: airton_sensor id: airton_sensor
lambda: "return 21;" lambda: "return 21;"
switch:
- platform: airton
sleep:
name: "sleep mode"
display:
name: "display light"