3#ifdef USE_TOSHIBA_RAS_25J2KVSG_ND
16static constexpr const char *
const CUSTOM_FANS[] PROGMEM = {
"Low+",
"Medium+"};
17static constexpr const char *
const CUSTOM_PRESETS[] PROGMEM = {
18 "Fireplace 1",
"Fireplace 2",
"Frost protection",
"HADA Care",
"Sleep 1h",
"Sleep 3h",
"Sleep 5h",
"Sleep 9h",
20static constexpr uint8_t SLEEP_HOURS[] PROGMEM = {1, 3, 5, 9};
22static constexpr uint32_t REPEAT_MS = 200;
23static constexpr uint8_t MAX_FRAME_LENGTH = 12;
24static constexpr uint8_t MOTION_FRAME_LENGTH = 7;
25static constexpr uint8_t SHORT_MOTION_COMMAND = 0x21;
26static constexpr uint8_t FIREPLACE_1 = 0x02;
27static constexpr uint8_t FIREPLACE_2 = 0x03;
28static constexpr uint8_t FEATURE_FROST = 0x04;
29static constexpr uint8_t SILENT_1 = 0x02;
30static constexpr uint8_t SILENT_2 = 0x0A;
31static constexpr uint8_t HADA_PREFIX = 0x21;
32static constexpr uint8_t HADA_SUFFIX = 0x17;
33static constexpr uint8_t SLEEP_TIMER_MARKER = 0x7F;
34static constexpr uint8_t SLEEP_TIMER_FLAGS = 0x03;
35static constexpr float FROST_TEMP_C_DEFAULT = 8.0f;
37static constexpr uint8_t FROST_TEMP_CODE_BASE = 1;
39static float target_min(
bool frost) {
42static float target_max(
bool frost) {
46static float clamp_target(
float target,
bool frost) {
return clamp(target, target_min(frost), target_max(frost)); }
47static uint8_t temperature_code_base(
bool frost) {
50static float decode_target(
const uint8_t *
message,
bool frost) {
51 return (
message[5] >> 4) + temperature_code_base(frost);
54static constexpr size_t frame_pulses(uint8_t nbytes, uint8_t repeat) {
return (repeat + 1u) * (16u * nbytes + 4u); }
75 auto preset = ToshibaPreset::TOSHIBA_PRESET_NONE;
86 preset = ToshibaPreset::TOSHIBA_PRESET_NONE;
88 if (
preset == ToshibaPreset::TOSHIBA_PRESET_HADA_CARE)
90 const bool frost =
preset == ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION;
94 std::isfinite(target) ? clamp_target(target, frost) : (frost ? FROST_TEMP_C_DEFAULT : 24.0f);
101 return ToshibaPreset::TOSHIBA_PRESET_BOOST;
103 return ToshibaPreset::TOSHIBA_PRESET_ECO;
105 return ToshibaPreset::TOSHIBA_PRESET_NONE;
111 for (
size_t i = 0; i < std::size(CUSTOM_PRESETS); i++) {
115 return ToshibaPreset::TOSHIBA_PRESET_NONE;
126 case ToshibaPreset::TOSHIBA_PRESET_NONE:
129 case ToshibaPreset::TOSHIBA_PRESET_BOOST:
132 case ToshibaPreset::TOSHIBA_PRESET_ECO:
141 if (
preset == ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION && previous !=
preset)
149 return TOSHIBA_FAN_SPEED_2;
151 return TOSHIBA_FAN_SPEED_4;
157 case TOSHIBA_FAN_SPEED_2:
160 case TOSHIBA_FAN_SPEED_4:
170 auto preset = previous_preset;
171 if (
call.get_preset().has_value()) {
173 }
else if (
call.has_custom_preset()) {
180 ESP_LOGW(TAG,
"This preset requires HEAT support");
184 if (
call.has_custom_preset() &&
call.get_mode().has_value()) {
185 ESP_LOGW(TAG,
"This preset requires HEAT mode");
188 if (
call.has_custom_preset()) {
191 preset = ToshibaPreset::TOSHIBA_PRESET_NONE;
195 preset = ToshibaPreset::TOSHIBA_PRESET_NONE;
200 preset == ToshibaPreset::TOSHIBA_PRESET_HADA_CARE) {
201 ESP_LOGW(TAG,
"SWING is unavailable during HADA Care");
205 const bool frost =
preset == ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION;
207 if (frost && previous_preset !=
preset) {
209 }
else if (!frost && previous_preset == ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION) {
214 ESP_LOGW(TAG,
"Target must be %s",
215 frost ? LOG_STR_LITERAL(
"5-13 C for frost protection") : LOG_STR_LITERAL(
"17-30 C for normal operation"));
225 }
else if (
call.has_custom_fan_mode()) {
234 if (
preset == ToshibaPreset::TOSHIBA_PRESET_HADA_CARE)
239 const bool state_command =
call.get_mode().has_value() ||
call.get_target_temperature().has_value() ||
240 call.get_fan_mode().has_value() ||
call.has_custom_fan_mode() ||
241 call.get_preset().has_value() ||
call.has_custom_preset();
253 uint8_t
message[MAX_FRAME_LENGTH] = {0, 0, 0, 0, TOSHIBA_COMMAND_DEFAULT};
255 const bool frost =
preset == ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION;
257 message[5] = (
static_cast<uint8_t
>(this->
target_temperature) - temperature_code_base(frost)) << 4;
261 message[7] =
static_cast<uint8_t
>(power) << 6;
263 if (
preset == ToshibaPreset::TOSHIBA_PRESET_FIREPLACE_1) {
265 }
else if (
preset == ToshibaPreset::TOSHIBA_PRESET_FIREPLACE_2) {
267 }
else if (
preset == ToshibaPreset::TOSHIBA_PRESET_HADA_CARE) {
273 message[4] = TOSHIBA_COMMAND_DEFAULT | TOSHIBA_COMMAND_TIMER | TOSHIBA_COMMAND_POWER;
276 &SLEEP_HOURS[
static_cast<uint8_t
>(
preset) -
static_cast<uint8_t
>(ToshibaPreset::TOSHIBA_PRESET_SLEEP_1H)])
278 message[9] = SLEEP_TIMER_MARKER;
279 message[10] = SLEEP_TIMER_FLAGS;
280 }
else if (
preset != ToshibaPreset::TOSHIBA_PRESET_NONE ||
is_silent(power)) {
282 message[4] = TOSHIBA_COMMAND_DEFAULT | TOSHIBA_COMMAND_POWER;
283 if (
preset == ToshibaPreset::TOSHIBA_PRESET_BOOST) {
284 message[8] = TOSHIBA_POWER_HIGH;
285 }
else if (
preset == ToshibaPreset::TOSHIBA_PRESET_ECO) {
286 message[8] = TOSHIBA_POWER_ECO;
295 auto *data = transmit.get_data();
297 data->reserve(frame_pulses(
length, 1) + (swing ? frame_pulses(MOTION_FRAME_LENGTH, 1) : 0));
308 transmit.
get_data()->
reserve(frame_pulses(MOTION_FRAME_LENGTH, repeat));
311 this->
perform_(transmit, command == TOSHIBA_MOTION_SWING);
316 uint8_t
message[MOTION_FRAME_LENGTH] = {0, 0, 0, 0, SHORT_MOTION_COMMAND, command};
325 duration_us +=
static_cast<uint32_t>(std::abs(pulse));
334 if (command != TOSHIBA_MOTION_SWING)
335 return command == TOSHIBA_MOTION_VANE_STEP;
339 if (remaining != 0 && remaining <= UINT16_MAX)
351 const uint8_t feature =
message[7] & 0x03;
353 (
message[7] & 0x3C) != 0 || (
message[7] & 0xC0) == 0xC0 || feature == 1)
356 preset = ToshibaPreset::TOSHIBA_PRESET_NONE;
359 if (
length != 9 && feature != 0)
362 if (
message[4] != TOSHIBA_COMMAND_DEFAULT)
364 if (feature == FIREPLACE_1) {
365 preset = ToshibaPreset::TOSHIBA_PRESET_FIREPLACE_1;
366 }
else if (feature == FIREPLACE_2) {
367 preset = ToshibaPreset::TOSHIBA_PRESET_FIREPLACE_2;
369 }
else if (
length == 10 &&
message[4] == (TOSHIBA_COMMAND_DEFAULT | TOSHIBA_COMMAND_POWER)) {
371 case TOSHIBA_POWER_HIGH:
372 preset = ToshibaPreset::TOSHIBA_PRESET_BOOST;
374 case TOSHIBA_POWER_ECO:
375 preset = ToshibaPreset::TOSHIBA_PRESET_ECO;
378 preset = ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION;
390 }
else if (
length == 11 &&
message[4] == TOSHIBA_COMMAND_DEFAULT &&
message[8] == HADA_PREFIX &&
392 preset = ToshibaPreset::TOSHIBA_PRESET_HADA_CARE;
393 }
else if (
length == 12 &&
message[4] == (TOSHIBA_COMMAND_DEFAULT | TOSHIBA_COMMAND_TIMER | TOSHIBA_COMMAND_POWER) &&
398 if (i == std::size(SLEEP_HOURS))
400 preset =
static_cast<ToshibaPreset
>(
static_cast<uint8_t
>(ToshibaPreset::TOSHIBA_PRESET_SLEEP_1H) + i);
404 const bool frost =
preset == ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION;
413 bool received =
false;
415 uint8_t
message[MAX_FRAME_LENGTH]{};
416 if (!data.expect_item(TOSHIBA_HEADER_MARK, TOSHIBA_HEADER_SPACE) ||
426 for (uint8_t i = 0; i <
length; i++)
436 }
while (data.expect_space(TOSHIBA_GAP_SPACE) && data.is_valid());
441 if (
length == MOTION_FRAME_LENGTH)
455 }
else if (previous_preset == ToshibaPreset::TOSHIBA_PRESET_FROST_PROTECTION) {
459 if (
preset == ToshibaPreset::TOSHIBA_PRESET_HADA_CARE)
468 if (this->
toshiba_preset_() == ToshibaPreset::TOSHIBA_PRESET_HADA_CARE) {
469 ESP_LOGW(TAG,
"Vane step is unavailable during HADA Care");
488 if (
is_silent(power) &&
preset != ToshibaPreset::TOSHIBA_PRESET_NONE) {
489 ESP_LOGW(TAG,
"Outdoor Silent with a preset is not supported");
505 if (this->power_selection_select_ !=
nullptr)
506 this->power_selection_select_->publish_state(
static_cast<size_t>(this->
power_selection_));
ESPPreferenceObject make_entity_preference(uint32_t version=0)
Create a preference object for storing this entity's state/settings.
constexpr size_t count(ValueType value) const
Check if the set contains a specific value (std::set compatibility) Returns 1 if present,...
This class is used to encode all control actions on a climate device.
const optional< ClimateSwingMode > & get_swing_mode() const
const optional< ClimateFanMode > & get_fan_mode() const
StringRef get_custom_fan_mode() const
ClimateMode mode
The active mode of the climate device.
float target_temperature
The target temperature of the climate device.
ClimateSwingMode swing_mode
The active swing mode of the climate device.
void set_supported_custom_presets_static(const char *const *presets, size_t count)
Points at a static table that outlives the climate (codegen); call before any runtime copy.
bool set_preset_(ClimatePreset preset)
Set preset. Reset custom preset. Return true if preset has been changed.
bool set_custom_preset_(const char *preset)
Set custom preset. Reset primary preset. Return true if preset has been changed.
bool set_fan_mode_(ClimateFanMode mode)
Set fan mode. Reset custom fan mode. Return true if fan mode has been changed.
bool has_custom_preset() const
Check if a custom preset is currently active.
void set_supported_custom_fan_modes_static(const char *const *modes, size_t count)
Points at a static table that outlives the climate (codegen); call before any runtime copy.
StringRef get_custom_preset() const
Get the active custom preset (read-only access). Returns StringRef.
void publish_state()
Publish the state of the climate device, to be called from integrations.
optional< ClimatePreset > preset
The active preset of the climate device.
bool set_custom_fan_mode_(const char *mode)
Set custom fan mode. Reset primary fan mode. Return true if fan mode has been changed.
StringRef get_custom_fan_mode() const
Get the active custom fan mode (read-only access). Returns StringRef.
climate::ClimatePresetMask presets_
climate::ClimateModeMask modes_
const RawTimings & get_data() const
void reserve(uint32_t len)
RemoteTransmitterBase * transmitter_
RemoteTransmitData * get_data()
static bool frame_header_valid(const uint8_t *message)
void encode_(remote_base::RemoteTransmitData *data, const uint8_t *message, uint8_t nbytes, uint8_t repeat)
static void finalize_frame(uint8_t *message, uint8_t length)
static climate::ClimateFanMode decode_fan(uint8_t code)
uint8_t encode_fan_() const
uint8_t encode_mode_() const
bool decode_(remote_base::RemoteReceiveData *data, uint8_t *message, uint8_t nbytes)
void decode_mode_(const uint8_t *message)
climate::ClimateSwingMode last_swing_mode_
static bool frame_checksum_valid(const uint8_t *message, uint8_t length)
bool receive_motion_(uint8_t command)
uint32_t swing_ignore_until_
void transmit_motion_(uint8_t command, uint8_t repeat)
void perform_(remote_base::RemoteTransmitterBase::TransmitCall &transmit, bool swing)
uint32_t last_frame_hash_
static ToshibaPreset custom_preset_at(size_t index)
bool on_receive(remote_base::RemoteReceiveData data) override
bool store_power_selection_(ToshibaPowerSelection power)
void publish_power_selection_()
static bool power_preset_supported(ToshibaPowerSelection power, ToshibaPreset preset)
static bool is_sleep_preset(ToshibaPreset preset)
void transmit_state() override
static bool parse_state(const uint8_t *message, uint8_t length, ToshibaPreset &preset, ToshibaPowerSelection &power)
ToshibaPowerSelection power_selection_
ESPPreferenceObject power_selection_pref_
void set_power_selection(size_t index)
void transmit_frame_(ToshibaPreset preset)
void apply_toshiba_preset_(ToshibaPreset preset)
bool receive_frame_(const uint8_t *message, uint8_t length)
void sanitize_restore_state(climate::ClimateDeviceRestoreState &restore) override
void enter_preset_(ToshibaPreset previous, ToshibaPreset preset)
static ToshibaPreset preset_from_climate(climate::ClimatePreset preset)
static bool is_silent(ToshibaPowerSelection power)
void apply_fan_code_(uint8_t code)
static constexpr uint32_t POWER_SELECTION_RESTORE_VERSION
uint8_t normal_target_temperature_
ToshibaPreset toshiba_preset_() const
uint8_t fan_code_() const
static size_t custom_preset_index(ToshibaPreset preset)
void encode_motion_(remote_base::RemoteTransmitData *data, uint8_t command, uint8_t repeat)
void control(const climate::ClimateCall &call) override
static ToshibaPreset preset_from_custom(const char *custom_preset)
@ TOSHIBA_POWER_SELECTION_SILENT_1
@ TOSHIBA_POWER_SELECTION_100
@ TOSHIBA_POWER_SELECTION_50
@ TOSHIBA_POWER_SELECTION_COUNT
@ TOSHIBA_POWER_SELECTION_SILENT_2
@ TOSHIBA_POWER_SELECTION_75
static bool is_heating_preset(ToshibaPreset preset)
const LogString * message
ClimatePreset
Enum for all preset modes NOTE: If adding values, update ClimatePresetMask in climate_traits....
@ CLIMATE_PRESET_NONE
No preset is active.
@ CLIMATE_PRESET_BOOST
Device is in boost preset.
@ CLIMATE_PRESET_ECO
Device is running an energy-saving preset.
@ CLIMATE_SWING_OFF
The swing mode is set to Off.
@ CLIMATE_SWING_VERTICAL
The fan mode is set to Vertical.
@ CLIMATE_MODE_DRY
The climate device is set to dry/humidity mode.
@ CLIMATE_MODE_FAN_ONLY
The climate device only has the fan enabled, no heating or cooling is taking place.
@ CLIMATE_MODE_HEAT
The climate device is set to heat to reach the target temperature.
@ CLIMATE_MODE_OFF
The climate device is off.
@ CLIMATE_FAN_AUTO
The fan mode is set to Auto.
const float TOSHIBA_GENERIC_TEMP_C_MAX
const float TOSHIBA_GENERIC_TEMP_C_MIN
ESPHOME_LOG_TAG(TAG, "toshiba.climate")
constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str)
Extend a FNV-1a hash with additional string data.
constexpr uint32_t FNV1_OFFSET_BASIS
FNV-1 32-bit offset basis.
const char * progmem_read_ptr(const char *const *addr)
uint32_t IRAM_ATTR HOT millis()
uint8_t progmem_read_byte(const uint8_t *addr)
Struct used to save the state of the climate device in restore memory.
ClimateSwingMode swing_mode