mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Add climate.hitachi_ac344 (#1336)
* Add climate.hitachi_ac344 * Add Hitachi AC344 Climate IR test * Fixes unhandled switch-case in fan-mode * Fixes logging format * Fixes file mode * Lint clang-tidy * Lint clang-tidy * Static cast float to uint8 git push * Remove comment and debug code * Change log verbosity to VV
This commit is contained in:
		
							
								
								
									
										0
									
								
								esphome/components/hitachi_ac344/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/hitachi_ac344/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										18
									
								
								esphome/components/hitachi_ac344/climate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/hitachi_ac344/climate.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import climate_ir | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  | AUTO_LOAD = ['climate_ir'] | ||||||
|  |  | ||||||
|  | hitachi_ac344_ns = cg.esphome_ns.namespace('hitachi_ac344') | ||||||
|  | HitachiClimate = hitachi_ac344_ns.class_('HitachiClimate', climate_ir.ClimateIR) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ | ||||||
|  |     cv.GenerateID(): cv.declare_id(HitachiClimate), | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     yield climate_ir.register_climate_ir(var, config) | ||||||
							
								
								
									
										365
									
								
								esphome/components/hitachi_ac344/hitachi_ac344.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								esphome/components/hitachi_ac344/hitachi_ac344.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,365 @@ | |||||||
|  | #include "hitachi_ac344.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace hitachi_ac344 { | ||||||
|  |  | ||||||
|  | static const char *TAG = "climate.hitachi_ac344"; | ||||||
|  |  | ||||||
|  | void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, const uint8_t data) { | ||||||
|  |   if (offset >= 8 || !nbits) | ||||||
|  |     return;  // Short circuit as it won't change. | ||||||
|  |   // Calculate the mask for the supplied value. | ||||||
|  |   uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); | ||||||
|  |   // Calculate the mask & clear the space for the data. | ||||||
|  |   // Clear the destination bits. | ||||||
|  |   *dst &= ~(uint8_t)(mask << offset); | ||||||
|  |   // Merge in the data. | ||||||
|  |   *dst |= ((data & mask) << offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void set_bit(uint8_t *const data, const uint8_t position, const bool on) { | ||||||
|  |   uint8_t mask = 1 << position; | ||||||
|  |   if (on) | ||||||
|  |     *data |= mask; | ||||||
|  |   else | ||||||
|  |     *data &= ~mask; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t *invert_byte_pairs(uint8_t *ptr, const uint16_t length) { | ||||||
|  |   for (uint16_t i = 1; i < length; i += 2) { | ||||||
|  |     // Code done this way to avoid a compiler warning bug. | ||||||
|  |     uint8_t inv = ~*(ptr + i - 1); | ||||||
|  |     *(ptr + i) = inv; | ||||||
|  |   } | ||||||
|  |   return ptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::get_power_() { return remote_state_[HITACHI_AC344_POWER_BYTE] == HITACHI_AC344_POWER_ON; } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_power_(bool on) { | ||||||
|  |   set_button_(HITACHI_AC344_BUTTON_POWER); | ||||||
|  |   remote_state_[HITACHI_AC344_POWER_BYTE] = on ? HITACHI_AC344_POWER_ON : HITACHI_AC344_POWER_OFF; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t HitachiClimate::get_mode_() { return remote_state_[HITACHI_AC344_MODE_BYTE] & 0xF; } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_mode_(uint8_t mode) { | ||||||
|  |   uint8_t new_mode = mode; | ||||||
|  |   switch (mode) { | ||||||
|  |     // Fan mode sets a special temp. | ||||||
|  |     case HITACHI_AC344_MODE_FAN: | ||||||
|  |       set_temp_(HITACHI_AC344_TEMP_FAN, false); | ||||||
|  |       break; | ||||||
|  |     case HITACHI_AC344_MODE_HEAT: | ||||||
|  |     case HITACHI_AC344_MODE_COOL: | ||||||
|  |     case HITACHI_AC344_MODE_DRY: | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       new_mode = HITACHI_AC344_MODE_COOL; | ||||||
|  |   } | ||||||
|  |   set_bits(&remote_state_[HITACHI_AC344_MODE_BYTE], 0, 4, new_mode); | ||||||
|  |   if (new_mode != HITACHI_AC344_MODE_FAN) | ||||||
|  |     set_temp_(previous_temp_); | ||||||
|  |   set_fan_(get_fan_());  // Reset the fan speed after the mode change. | ||||||
|  |   set_power_(true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_temp_(uint8_t celsius, bool set_previous) { | ||||||
|  |   uint8_t temp; | ||||||
|  |   temp = std::min(celsius, HITACHI_AC344_TEMP_MAX); | ||||||
|  |   temp = std::max(temp, HITACHI_AC344_TEMP_MIN); | ||||||
|  |   set_bits(&remote_state_[HITACHI_AC344_TEMP_BYTE], HITACHI_AC344_TEMP_OFFSET, HITACHI_AC344_TEMP_SIZE, temp); | ||||||
|  |   if (previous_temp_ > temp) | ||||||
|  |     set_button_(HITACHI_AC344_BUTTON_TEMP_DOWN); | ||||||
|  |   else if (previous_temp_ < temp) | ||||||
|  |     set_button_(HITACHI_AC344_BUTTON_TEMP_UP); | ||||||
|  |   if (set_previous) | ||||||
|  |     previous_temp_ = temp; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t HitachiClimate::get_fan_() { return remote_state_[HITACHI_AC344_FAN_BYTE] >> 4 & 0xF; } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_fan_(uint8_t speed) { | ||||||
|  |   uint8_t new_speed = std::max(speed, HITACHI_AC344_FAN_MIN); | ||||||
|  |   uint8_t fan_max = HITACHI_AC344_FAN_MAX; | ||||||
|  |  | ||||||
|  |   // Only 2 x low speeds in Dry mode or Auto | ||||||
|  |   if (get_mode_() == HITACHI_AC344_MODE_DRY && speed == HITACHI_AC344_FAN_AUTO) { | ||||||
|  |     fan_max = HITACHI_AC344_FAN_AUTO; | ||||||
|  |   } else if (get_mode_() == HITACHI_AC344_MODE_DRY) { | ||||||
|  |     fan_max = HITACHI_AC344_FAN_MAX_DRY; | ||||||
|  |   } else if (get_mode_() == HITACHI_AC344_MODE_FAN && speed == HITACHI_AC344_FAN_AUTO) { | ||||||
|  |     // Fan Mode does not have auto. Set to safe low | ||||||
|  |     new_speed = HITACHI_AC344_FAN_MIN; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   new_speed = std::min(new_speed, fan_max); | ||||||
|  |   // Handle the setting the button value if we are going to change the value. | ||||||
|  |   if (new_speed != get_fan_()) | ||||||
|  |     set_button_(HITACHI_AC344_BUTTON_FAN); | ||||||
|  |   // Set the values | ||||||
|  |  | ||||||
|  |   set_bits(&remote_state_[HITACHI_AC344_FAN_BYTE], 4, 4, new_speed); | ||||||
|  |   remote_state_[9] = 0x92; | ||||||
|  |  | ||||||
|  |   // When fan is at min/max, additional bytes seem to be set | ||||||
|  |   if (new_speed == HITACHI_AC344_FAN_MIN) | ||||||
|  |     remote_state_[9] = 0x98; | ||||||
|  |   remote_state_[29] = 0x01; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_swing_v_toggle_(bool on) { | ||||||
|  |   uint8_t button = get_button_();  // Get the current button value. | ||||||
|  |   if (on) | ||||||
|  |     button = HITACHI_AC344_BUTTON_SWINGV;          // Set the button to SwingV. | ||||||
|  |   else if (button == HITACHI_AC344_BUTTON_SWINGV)  // Asked to unset it | ||||||
|  |     // It was set previous, so use Power as a default | ||||||
|  |     button = HITACHI_AC344_BUTTON_POWER; | ||||||
|  |   set_button_(button); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::get_swing_v_toggle_() { return get_button_() == HITACHI_AC344_BUTTON_SWINGV; } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_swing_v_(bool on) { | ||||||
|  |   set_swing_v_toggle_(on);  // Set the button value. | ||||||
|  |   set_bit(&remote_state_[HITACHI_AC344_SWINGV_BYTE], HITACHI_AC344_SWINGV_OFFSET, on); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::get_swing_v_() { | ||||||
|  |   return GETBIT8(remote_state_[HITACHI_AC344_SWINGV_BYTE], HITACHI_AC344_SWINGV_OFFSET); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_swing_h_(uint8_t position) { | ||||||
|  |   if (position > HITACHI_AC344_SWINGH_LEFT_MAX) | ||||||
|  |     return set_swing_h_(HITACHI_AC344_SWINGH_MIDDLE); | ||||||
|  |   set_bits(&remote_state_[HITACHI_AC344_SWINGH_BYTE], HITACHI_AC344_SWINGH_OFFSET, HITACHI_AC344_SWINGH_SIZE, position); | ||||||
|  |   set_button_(HITACHI_AC344_BUTTON_SWINGH); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t HitachiClimate::get_swing_h_() { | ||||||
|  |   return GETBITS8(remote_state_[HITACHI_AC344_SWINGH_BYTE], HITACHI_AC344_SWINGH_OFFSET, HITACHI_AC344_SWINGH_SIZE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t HitachiClimate::get_button_() { return remote_state_[HITACHI_AC344_BUTTON_BYTE]; } | ||||||
|  |  | ||||||
|  | void HitachiClimate::set_button_(uint8_t button) { remote_state_[HITACHI_AC344_BUTTON_BYTE] = button; } | ||||||
|  |  | ||||||
|  | void HitachiClimate::transmit_state() { | ||||||
|  |   switch (this->mode) { | ||||||
|  |     case climate::CLIMATE_MODE_COOL: | ||||||
|  |       set_mode_(HITACHI_AC344_MODE_COOL); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_DRY: | ||||||
|  |       set_mode_(HITACHI_AC344_MODE_DRY); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_HEAT: | ||||||
|  |       set_mode_(HITACHI_AC344_MODE_HEAT); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_AUTO: | ||||||
|  |       set_mode_(HITACHI_AC344_MODE_AUTO); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_FAN_ONLY: | ||||||
|  |       set_mode_(HITACHI_AC344_MODE_FAN); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_OFF: | ||||||
|  |       set_power_(false); | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   set_temp_(static_cast<uint8_t>(this->target_temperature)); | ||||||
|  |  | ||||||
|  |   switch (this->fan_mode) { | ||||||
|  |     case climate::CLIMATE_FAN_LOW: | ||||||
|  |       set_fan_(HITACHI_AC344_FAN_LOW); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_FAN_MEDIUM: | ||||||
|  |       set_fan_(HITACHI_AC344_FAN_MEDIUM); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_FAN_HIGH: | ||||||
|  |       set_fan_(HITACHI_AC344_FAN_HIGH); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_FAN_ON: | ||||||
|  |     case climate::CLIMATE_FAN_AUTO: | ||||||
|  |     default: | ||||||
|  |       set_fan_(HITACHI_AC344_FAN_AUTO); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   switch (this->swing_mode) { | ||||||
|  |     case climate::CLIMATE_SWING_BOTH: | ||||||
|  |       set_swing_v_(true); | ||||||
|  |       set_swing_h_(HITACHI_AC344_SWINGH_AUTO); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_VERTICAL: | ||||||
|  |       set_swing_v_(true); | ||||||
|  |       set_swing_h_(HITACHI_AC344_SWINGH_MIDDLE); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_HORIZONTAL: | ||||||
|  |       set_swing_v_(false); | ||||||
|  |       set_swing_h_(HITACHI_AC344_SWINGH_AUTO); | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_OFF: | ||||||
|  |       set_swing_v_(false); | ||||||
|  |       set_swing_h_(HITACHI_AC344_SWINGH_MIDDLE); | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // TODO: find change value to set button, now always set to power button | ||||||
|  |   set_button_(HITACHI_AC344_BUTTON_POWER); | ||||||
|  |  | ||||||
|  |   invert_byte_pairs(remote_state_ + 3, HITACHI_AC344_STATE_LENGTH - 3); | ||||||
|  |  | ||||||
|  |   auto transmit = this->transmitter_->transmit(); | ||||||
|  |   auto data = transmit.get_data(); | ||||||
|  |   data->set_carrier_frequency(HITACHI_AC344_FREQ); | ||||||
|  |  | ||||||
|  |   uint8_t repeat = 0; | ||||||
|  |   for (uint8_t r = 0; r <= repeat; r++) { | ||||||
|  |     // Header | ||||||
|  |     data->item(HITACHI_AC344_HDR_MARK, HITACHI_AC344_HDR_SPACE); | ||||||
|  |     // Data | ||||||
|  |     for (uint8_t i : remote_state_) { | ||||||
|  |       for (uint8_t j = 0; j < 8; j++) { | ||||||
|  |         data->mark(HITACHI_AC344_BIT_MARK); | ||||||
|  |         bool bit = i & (1 << j); | ||||||
|  |         data->space(bit ? HITACHI_AC344_ONE_SPACE : HITACHI_AC344_ZERO_SPACE); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     // Footer | ||||||
|  |     data->item(HITACHI_AC344_BIT_MARK, HITACHI_AC344_MIN_GAP); | ||||||
|  |   } | ||||||
|  |   transmit.perform(); | ||||||
|  |  | ||||||
|  |   dump_state_("Sent", remote_state_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::parse_mode_(const uint8_t remote_state[]) { | ||||||
|  |   uint8_t power = remote_state[HITACHI_AC344_POWER_BYTE]; | ||||||
|  |   ESP_LOGV(TAG, "Power: %02X %02X", remote_state[HITACHI_AC344_POWER_BYTE], power); | ||||||
|  |   uint8_t mode = remote_state[HITACHI_AC344_MODE_BYTE] & 0xF; | ||||||
|  |   ESP_LOGV(TAG, "Mode: %02X %02X", remote_state[HITACHI_AC344_MODE_BYTE], mode); | ||||||
|  |   if (power == HITACHI_AC344_POWER_ON) { | ||||||
|  |     switch (mode) { | ||||||
|  |       case HITACHI_AC344_MODE_COOL: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_COOL; | ||||||
|  |         break; | ||||||
|  |       case HITACHI_AC344_MODE_DRY: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_DRY; | ||||||
|  |         break; | ||||||
|  |       case HITACHI_AC344_MODE_HEAT: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_HEAT; | ||||||
|  |         break; | ||||||
|  |       case HITACHI_AC344_MODE_AUTO: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_AUTO; | ||||||
|  |         break; | ||||||
|  |       case HITACHI_AC344_MODE_FAN: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_FAN_ONLY; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     this->mode = climate::CLIMATE_MODE_OFF; | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::parse_temperature_(const uint8_t remote_state[]) { | ||||||
|  |   uint8_t temperature = | ||||||
|  |       GETBITS8(remote_state[HITACHI_AC344_TEMP_BYTE], HITACHI_AC344_TEMP_OFFSET, HITACHI_AC344_TEMP_SIZE); | ||||||
|  |   this->target_temperature = temperature; | ||||||
|  |   ESP_LOGV(TAG, "Temperature: %02X %02u %04f", remote_state[HITACHI_AC344_TEMP_BYTE], temperature, | ||||||
|  |            this->target_temperature); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::parse_fan_(const uint8_t remote_state[]) { | ||||||
|  |   uint8_t fan_mode = remote_state[HITACHI_AC344_FAN_BYTE] >> 4 & 0xF; | ||||||
|  |   ESP_LOGV(TAG, "Fan: %02X %02X", remote_state[HITACHI_AC344_FAN_BYTE], fan_mode); | ||||||
|  |   switch (fan_mode) { | ||||||
|  |     case HITACHI_AC344_FAN_MIN: | ||||||
|  |     case HITACHI_AC344_FAN_LOW: | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_LOW; | ||||||
|  |       break; | ||||||
|  |     case HITACHI_AC344_FAN_MEDIUM: | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_MEDIUM; | ||||||
|  |       break; | ||||||
|  |     case HITACHI_AC344_FAN_HIGH: | ||||||
|  |     case HITACHI_AC344_FAN_MAX: | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_HIGH; | ||||||
|  |       break; | ||||||
|  |     case HITACHI_AC344_FAN_AUTO: | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_AUTO; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::parse_swing_(const uint8_t remote_state[]) { | ||||||
|  |   uint8_t swing_modeh = | ||||||
|  |       GETBITS8(remote_state[HITACHI_AC344_SWINGH_BYTE], HITACHI_AC344_SWINGH_OFFSET, HITACHI_AC344_SWINGH_SIZE); | ||||||
|  |   ESP_LOGV(TAG, "SwingH: %02X %02X", remote_state[HITACHI_AC344_SWINGH_BYTE], swing_modeh); | ||||||
|  |  | ||||||
|  |   if ((swing_modeh & 0x7) == 0x0) { | ||||||
|  |     this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; | ||||||
|  |   } else if ((swing_modeh & 0x3) == 0x3) { | ||||||
|  |     this->swing_mode = climate::CLIMATE_SWING_OFF; | ||||||
|  |   } else { | ||||||
|  |     this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool HitachiClimate::on_receive(remote_base::RemoteReceiveData data) { | ||||||
|  |   // Validate header | ||||||
|  |   if (!data.expect_item(HITACHI_AC344_HDR_MARK, HITACHI_AC344_HDR_SPACE)) { | ||||||
|  |     ESP_LOGVV(TAG, "Header fail"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   uint8_t recv_state[HITACHI_AC344_STATE_LENGTH] = {0}; | ||||||
|  |   // Read all bytes. | ||||||
|  |   for (uint8_t pos = 0; pos < HITACHI_AC344_STATE_LENGTH; pos++) { | ||||||
|  |     // Read bit | ||||||
|  |     for (int8_t bit = 0; bit < 8; bit++) { | ||||||
|  |       if (data.expect_item(HITACHI_AC344_BIT_MARK, HITACHI_AC344_ONE_SPACE)) | ||||||
|  |         recv_state[pos] |= 1 << bit; | ||||||
|  |       else if (!data.expect_item(HITACHI_AC344_BIT_MARK, HITACHI_AC344_ZERO_SPACE)) { | ||||||
|  |         ESP_LOGVV(TAG, "Byte %d bit %d fail", pos, bit); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Validate footer | ||||||
|  |   if (!data.expect_mark(HITACHI_AC344_BIT_MARK)) { | ||||||
|  |     ESP_LOGVV(TAG, "Footer fail"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   dump_state_("Recv", recv_state); | ||||||
|  |  | ||||||
|  |   // parse mode | ||||||
|  |   this->parse_mode_(recv_state); | ||||||
|  |   // parse temperature | ||||||
|  |   this->parse_temperature_(recv_state); | ||||||
|  |   // parse fan | ||||||
|  |   this->parse_fan_(recv_state); | ||||||
|  |   // parse swingv | ||||||
|  |   this->parse_swing_(recv_state); | ||||||
|  |   this->publish_state(); | ||||||
|  |   for (uint8_t i = 0; i < HITACHI_AC344_STATE_LENGTH; i++) | ||||||
|  |     remote_state_[i] = recv_state[i]; | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void HitachiClimate::dump_state_(const char action[], uint8_t state[]) { | ||||||
|  |   for (uint16_t i = 0; i < HITACHI_AC344_STATE_LENGTH - 10; i += 10) { | ||||||
|  |     ESP_LOGV(TAG, "%s: %02X %02X %02X %02X %02X  %02X %02X %02X %02X %02X", action, state[i + 0], state[i + 1], | ||||||
|  |              state[i + 2], state[i + 3], state[i + 4], state[i + 5], state[i + 6], state[i + 7], state[i + 8], | ||||||
|  |              state[i + 9]); | ||||||
|  |   } | ||||||
|  |   ESP_LOGV(TAG, "%s: %02X %02X %02X", action, state[40], state[41], state[42]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace hitachi_ac344 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										122
									
								
								esphome/components/hitachi_ac344/hitachi_ac344.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								esphome/components/hitachi_ac344/hitachi_ac344.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | #include "esphome/components/climate_ir/climate_ir.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace hitachi_ac344 { | ||||||
|  |  | ||||||
|  | const uint16_t HITACHI_AC344_HDR_MARK = 3300;   // ac | ||||||
|  | const uint16_t HITACHI_AC344_HDR_SPACE = 1700;  // ac | ||||||
|  | const uint16_t HITACHI_AC344_BIT_MARK = 400; | ||||||
|  | const uint16_t HITACHI_AC344_ONE_SPACE = 1250; | ||||||
|  | const uint16_t HITACHI_AC344_ZERO_SPACE = 500; | ||||||
|  | const uint32_t HITACHI_AC344_MIN_GAP = 100000;  // just a guess. | ||||||
|  | const uint16_t HITACHI_AC344_FREQ = 38000;      // Hz. | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_BYTE = 11; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_POWER = 0x13; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_SLEEP = 0x31; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_MODE = 0x41; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_FAN = 0x42; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_TEMP_DOWN = 0x43; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_TEMP_UP = 0x44; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_SWINGV = 0x81; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_SWINGH = 0x8C; | ||||||
|  | const uint8_t HITACHI_AC344_BUTTON_MILDEWPROOF = 0xE2; | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_TEMP_BYTE = 13; | ||||||
|  | const uint8_t HITACHI_AC344_TEMP_OFFSET = 2; | ||||||
|  | const uint8_t HITACHI_AC344_TEMP_SIZE = 6; | ||||||
|  | const uint8_t HITACHI_AC344_TEMP_MIN = 16;  // 16C | ||||||
|  | const uint8_t HITACHI_AC344_TEMP_MAX = 32;  // 32C | ||||||
|  | const uint8_t HITACHI_AC344_TEMP_FAN = 27;  // 27C | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_TIMER_BYTE = 15; | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_MODE_BYTE = 25; | ||||||
|  | const uint8_t HITACHI_AC344_MODE_FAN = 1; | ||||||
|  | const uint8_t HITACHI_AC344_MODE_COOL = 3; | ||||||
|  | const uint8_t HITACHI_AC344_MODE_DRY = 5; | ||||||
|  | const uint8_t HITACHI_AC344_MODE_HEAT = 6; | ||||||
|  | const uint8_t HITACHI_AC344_MODE_AUTO = 7; | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_FAN_BYTE = HITACHI_AC344_MODE_BYTE; | ||||||
|  | const uint8_t HITACHI_AC344_FAN_MIN = 1; | ||||||
|  | const uint8_t HITACHI_AC344_FAN_LOW = 2; | ||||||
|  | const uint8_t HITACHI_AC344_FAN_MEDIUM = 3; | ||||||
|  | const uint8_t HITACHI_AC344_FAN_HIGH = 4; | ||||||
|  | const uint8_t HITACHI_AC344_FAN_AUTO = 5; | ||||||
|  | const uint8_t HITACHI_AC344_FAN_MAX = 6; | ||||||
|  | const uint8_t HITACHI_AC344_FAN_MAX_DRY = 2; | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_POWER_BYTE = 27; | ||||||
|  | const uint8_t HITACHI_AC344_POWER_ON = 0xF1; | ||||||
|  | const uint8_t HITACHI_AC344_POWER_OFF = 0xE1; | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_BYTE = 35; | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_OFFSET = 0;     // Mask 0b00000xxx | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_SIZE = 3;       // Mask 0b00000xxx | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_AUTO = 0;       // 0b000 | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_RIGHT_MAX = 1;  // 0b001 | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_RIGHT = 2;      // 0b010 | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_MIDDLE = 3;     // 0b011 | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_LEFT = 4;       // 0b100 | ||||||
|  | const uint8_t HITACHI_AC344_SWINGH_LEFT_MAX = 5;   // 0b101 | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_SWINGV_BYTE = 37; | ||||||
|  | const uint8_t HITACHI_AC344_SWINGV_OFFSET = 5;  // Mask 0b00x00000 | ||||||
|  |  | ||||||
|  | const uint8_t HITACHI_AC344_MILDEWPROOF_BYTE = HITACHI_AC344_SWINGV_BYTE; | ||||||
|  | const uint8_t HITACHI_AC344_MILDEWPROOF_OFFSET = 2;  // Mask 0b00000x00 | ||||||
|  |  | ||||||
|  | const uint16_t HITACHI_AC344_STATE_LENGTH = 43; | ||||||
|  | const uint16_t HITACHI_AC344_BITS = HITACHI_AC344_STATE_LENGTH * 8; | ||||||
|  |  | ||||||
|  | #define GETBIT8(a, b) (a & ((uint8_t) 1 << b)) | ||||||
|  | #define GETBITS8(data, offset, size) (((data) & (((uint8_t) UINT8_MAX >> (8 - (size))) << (offset))) >> (offset)) | ||||||
|  |  | ||||||
|  | class HitachiClimate : public climate_ir::ClimateIR { | ||||||
|  |  public: | ||||||
|  |   HitachiClimate() | ||||||
|  |       : climate_ir::ClimateIR( | ||||||
|  |             HITACHI_AC344_TEMP_MIN, HITACHI_AC344_TEMP_MAX, 1.0F, true, true, | ||||||
|  |             std::vector<climate::ClimateFanMode>{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, | ||||||
|  |                                                  climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH}, | ||||||
|  |             std::vector<climate::ClimateSwingMode>{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL}) {} | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint8_t remote_state_[HITACHI_AC344_STATE_LENGTH]{0x01, 0x10, 0x00, 0x40, 0x00, 0xFF, 0x00, 0xCC, 0x00, 0x00, 0x00, | ||||||
|  |                                                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
|  |                                                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, | ||||||
|  |                                                     0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||||
|  |   uint8_t previous_temp_{27}; | ||||||
|  |   // Transmit via IR the state of this climate controller. | ||||||
|  |   void transmit_state() override; | ||||||
|  |   bool get_power_(); | ||||||
|  |   void set_power_(bool on); | ||||||
|  |   uint8_t get_mode_(); | ||||||
|  |   void set_mode_(uint8_t mode); | ||||||
|  |   void set_temp_(uint8_t celsius, bool set_previous = false); | ||||||
|  |   uint8_t get_fan_(); | ||||||
|  |   void set_fan_(uint8_t speed); | ||||||
|  |   void set_swing_v_toggle_(bool on); | ||||||
|  |   bool get_swing_v_toggle_(); | ||||||
|  |   void set_swing_v_(bool on); | ||||||
|  |   bool get_swing_v_(); | ||||||
|  |   void set_swing_h_(uint8_t position); | ||||||
|  |   uint8_t get_swing_h_(); | ||||||
|  |   uint8_t get_button_(); | ||||||
|  |   void set_button_(uint8_t button); | ||||||
|  |   // Handle received IR Buffer | ||||||
|  |   bool on_receive(remote_base::RemoteReceiveData data) override; | ||||||
|  |   bool parse_mode_(const uint8_t remote_state[]); | ||||||
|  |   bool parse_temperature_(const uint8_t remote_state[]); | ||||||
|  |   bool parse_fan_(const uint8_t remote_state[]); | ||||||
|  |   bool parse_swing_(const uint8_t remote_state[]); | ||||||
|  |   bool parse_state_frame_(const uint8_t frame[]); | ||||||
|  |   void dump_state_(const char action[], uint8_t remote_state[]); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace hitachi_ac344 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -1321,6 +1321,8 @@ climate: | |||||||
|     name: LG Climate |     name: LG Climate | ||||||
|   - platform: toshiba |   - platform: toshiba | ||||||
|     name: Toshiba Climate |     name: Toshiba Climate | ||||||
|  |   - platform: hitachi_ac344 | ||||||
|  |     name: Hitachi Climate | ||||||
|  |  | ||||||
| switch: | switch: | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user