From 68658a9144f1e0570b054aab2acea1db80ce578d Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sat, 25 May 2024 19:02:53 -0700 Subject: [PATCH 01/15] Kumo Emulation Mode WIP --- esphome/components/mitsubishi_itp/climate.py | 38 ++++- .../mitsubishi_uart-climatecall.cpp | 21 +++ .../mitsubishi_uart-packetprocessing.cpp | 99 ++++++++++++- .../mitsubishi_itp/mitsubishi_uart.cpp | 9 ++ .../mitsubishi_itp/mitsubishi_uart.h | 23 ++++ .../mitsubishi_itp/muart_bridge.cpp | 17 ++- .../mitsubishi_itp/muart_packet-derived.cpp | 130 ++++++++++++++++-- .../components/mitsubishi_itp/muart_packet.h | 122 ++++++++++++++-- .../mitsubishi_itp/muart_rawpacket.cpp | 6 + .../mitsubishi_itp/muart_rawpacket.h | 10 +- .../components/mitsubishi_itp/muart_utils.h | 34 +++-- 11 files changed, 464 insertions(+), 45 deletions(-) diff --git a/esphome/components/mitsubishi_itp/climate.py b/esphome/components/mitsubishi_itp/climate.py index 8a9509e8cb9d..1ec41ea55aab 100644 --- a/esphome/components/mitsubishi_itp/climate.py +++ b/esphome/components/mitsubishi_itp/climate.py @@ -3,6 +3,7 @@ from esphome.components import ( climate, uart, + time, sensor, binary_sensor, button, @@ -19,11 +20,13 @@ CONF_SUPPORTED_MODES, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_FREQUENCY, + DEVICE_CLASS_HUMIDITY, ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_NONE, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, UNIT_HERTZ, + UNIT_PERCENT, ) from esphome.core import coroutine @@ -39,6 +42,7 @@ ] DEPENDENCIES = [ "uart", + "time", "climate", "sensor", "binary_sensor", @@ -49,8 +53,11 @@ CONF_UART_HEATPUMP = "uart_heatpump" CONF_UART_THERMOSTAT = "uart_thermostat" +CONF_TIME_SOURCE = "time_source" CONF_THERMOSTAT_TEMPERATURE = "thermostat_temperature" +CONF_THERMOSTAT_HUMIDITY = "thermostat_humidity" +CONF_THERMOSTAT_BATTERY = "thermostat_battery" CONF_ERROR_CODE = "error_code" CONF_ISEE_STATUS = "isee_status" @@ -104,6 +111,7 @@ cv.GenerateID(CONF_ID): cv.declare_id(MitsubishiUART), cv.Required(CONF_UART_HEATPUMP): cv.use_id(uart.UARTComponent), cv.Optional(CONF_UART_THERMOSTAT): cv.use_id(uart.UARTComponent), + cv.Optional(CONF_TIME_SOURCE): cv.use_id(time.RealTimeClock), # Overwrite name from ENTITY_BASE_SCHEMA with "Climate" as default cv.Optional(CONF_NAME, default="Climate"): cv.Any( cv.All( @@ -154,6 +162,23 @@ ), sensor.register_sensor, ), + CONF_THERMOSTAT_HUMIDITY: ( + "Thermostat Humidity", + sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=0, + ), + sensor.register_sensor + ), + CONF_THERMOSTAT_BATTERY: ( + "Thermostat Battery", + text_sensor.text_sensor_schema( + icon="mdi:battery", + ), + text_sensor.register_text_sensor + ), "compressor_frequency": ( "Compressor Frequency", sensor.sensor_schema( @@ -308,8 +333,14 @@ async def to_code(config): # Add sensor as source SELECTS[CONF_TEMPERATURE_SOURCE_SELECT][2].append("Thermostat") - # Traits + # If RTC defined + if CONF_TIME_SOURCE in config: + rtc_component = await cg.get_variable(config[CONF_TIME_SOURCE]) + cg.add(getattr(muart_component, "set_time_source")(rtc_component)) + elif CONF_UART_THERMOSTAT in config: + raise cv.RequiredFieldInvalid(f"{CONF_TIME_SOURCE} is required if {CONF_TS_UART} is set.") + # Traits traits = muart_component.config_traits() if CONF_SUPPORTED_MODES in config: @@ -329,9 +360,8 @@ async def to_code(config): registration_function, ) in SENSORS.items(): # Only add the thermostat temp if we have a TS_UART - if (sensor_designator == CONF_THERMOSTAT_TEMPERATURE) and ( - CONF_UART_THERMOSTAT not in config - ): + if ((CONF_UART_THERMOSTAT not in config) and + (sensor_designator in [CONF_THERMOSTAT_TEMPERATURE, CONF_THERMOSTAT_HUMIDITY, CONF_THERMOSTAT_BATTERY])): continue sensor_conf = config[CONF_SENSORS][sensor_designator] diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp index 1ac9cd5e8beb..6aac0196eec8 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp @@ -78,6 +78,27 @@ void MitsubishiUART::control(const climate::ClimateCall &call) { if (call.get_target_temperature().has_value()) { target_temperature = call.get_target_temperature().value(); set_request_packet.set_target_temperature(call.get_target_temperature().value()); + + // update our MHK tracking setpoints accordingly + switch (mode) { + case climate::CLIMATE_MODE_COOL: + case climate::CLIMATE_MODE_DRY: + this->last_cool_setpoint_ = target_temperature; + break; + case climate::CLIMATE_MODE_HEAT: + this->last_heat_setpoint_ = target_temperature; + break; + case climate::CLIMATE_MODE_HEAT_COOL: + if (this->get_traits().get_supports_two_point_target_temperature()) { + this->last_heat_setpoint_ = target_temperature_low; + this->last_cool_setpoint_ = target_temperature_high; + } else { + // this->last_heat_setpoint_ = target_temperature; + // this->last_cool_setpoint_ = target_temperature; + } + default: + break; + } } // TODO: diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index e631b0b8e32e..251f0892c87d 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -54,10 +54,17 @@ void MitsubishiUART::process_packet(const ExtendedConnectResponsePacket &packet) void MitsubishiUART::process_packet(const GetRequestPacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); - route_packet_(packet); - // These are just requests for information from the thermostat. For now, nothing to be done - // except route them. In the future, we could use this to inject information for the thermostat - // or use a cached value. + + switch (packet.get_requested_command()) { + case GetCommand::KUMO_GET_ADAPTER_STATE: + this->handle_kumo_adapter_state_get_request(packet); + break; + case GetCommand::KUMO_AB: + this->handle_kumo_aa_get_request(packet); + break; + default: + route_packet_(packet); + } } void MitsubishiUART::process_packet(const SettingsGetResponsePacket &packet) { @@ -111,6 +118,18 @@ void MitsubishiUART::process_packet(const SettingsGetResponsePacket &packet) { target_temperature = packet.get_target_temp(); publish_on_update_ |= (old_target_temperature != target_temperature); + switch (mode) { + case climate::CLIMATE_MODE_COOL: + case climate::CLIMATE_MODE_DRY: + this->last_cool_setpoint_ = target_temperature; + break; + case climate::CLIMATE_MODE_HEAT: + this->last_heat_setpoint_ = target_temperature; + break; + default: + break; + } + // Fan static bool fan_changed = false; switch (packet.get_fan()) { @@ -333,6 +352,13 @@ void MitsubishiUART::process_packet(const ErrorStateGetResponsePacket &packet) { publish_on_update_ |= (old_error_code != error_code_sensor_->raw_state); } +void MitsubishiUART::process_packet(const SettingsSetRequestPacket &packet) { + ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); + + // forward this packet as-is; we're just intercepting to log. + route_packet_(packet); +} + void MitsubishiUART::process_packet(const RemoteTemperatureSetRequestPacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); @@ -355,12 +381,75 @@ void MitsubishiUART::process_packet(const RemoteTemperatureSetRequestPacket &pac publish_on_update_ |= (old_thermostat_temp != thermostat_temperature_sensor_->raw_state); } -}; +} + +void MitsubishiUART::process_packet(const KumoThermostatSensorStatusPacket &packet) { + ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); + + if (thermostat_humidity_sensor_ && packet.get_flags() & 0x04) { + const float old_humidity = thermostat_humidity_sensor_->raw_state; + thermostat_humidity_sensor_->raw_state = packet.get_indoor_humidity_percent(); + publish_on_update_ |= (old_humidity != thermostat_humidity_sensor_->raw_state); + } + + if (thermostat_battery_sensor_ && packet.get_flags() & 0x08) { + const auto old_battery = thermostat_battery_sensor_->raw_state; + thermostat_battery_sensor_->raw_state = THERMOSTAT_BATTERY_STATE_NAMES[packet.get_thermostat_battery_state()]; + publish_on_update_ |= (old_battery != thermostat_battery_sensor_->raw_state); + } + + ts_bridge_->send_packet(SetResponsePacket()); +} + +void MitsubishiUART::process_packet(const KumoThermostatHelloPacket &packet) { + ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); + + ts_bridge_->send_packet(SetResponsePacket()); +} + +void MitsubishiUART::process_packet(const KumoThermostatStateSyncPacket &packet) { + ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); + + if (packet.get_flags() & 0x08) this->last_heat_setpoint_ = packet.get_heat_setpoint(); + if (packet.get_flags() & 0x10) this->last_cool_setpoint_ = packet.get_cool_setpoint(); + + ts_bridge_->send_packet(SetResponsePacket()); +} + +void MitsubishiUART::process_packet(const KumoAASetRequestPacket &packet) { + ESP_LOGV(TAG, "Processing inbound KumoAASetRequestPacket: %s", packet.to_string().c_str()); + + ts_bridge_->send_packet(SetResponsePacket()); +} + void MitsubishiUART::process_packet(const SetResponsePacket &packet) { ESP_LOGV(TAG, "Got Set Response packet, success = %s (code = %x)", packet.is_successful() ? "true" : "false", packet.get_result_code()); route_packet_(packet); } +// Process incoming data requests (Kumo) +void MitsubishiUART::handle_kumo_adapter_state_get_request(const GetRequestPacket &packet) { + auto response = KumoCloudStateSyncPacket(); + + response.set_heat_setpoint(this->last_heat_setpoint_); + response.set_cool_setpoint(this->last_cool_setpoint_); + + if (this->time_source != nullptr) { + response.set_timestamp(this->time_source->now()); + } else { + ESP_LOGW(TAG, "No time source specified. Cannot provide accurate time!"); + response.set_timestamp(ESPTime::from_epoch_utc(1704067200)); // 2024-01-01 00:00:00 Z + } + + ts_bridge_->send_packet(response); +} + +void MitsubishiUART::handle_kumo_aa_get_request(const GetRequestPacket &packet) { + auto response = KumoABGetRequestPacket(); + + ts_bridge_->send_packet(response); +} + } // namespace mitsubishi_itp } // namespace esphome diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp index c7e5eb875336..4558dcbd35c7 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp @@ -203,6 +203,11 @@ void MitsubishiUART::do_publish_() { ESP_LOGI(TAG, "Outdoor temp differs, do publish"); outdoor_temperature_sensor_->publish_state(outdoor_temperature_sensor_->raw_state); } + if (thermostat_humidity_sensor_ && + (thermostat_humidity_sensor_->raw_state != thermostat_humidity_sensor_->state)) { + ESP_LOGI(TAG, "Thermostat humidity differs, do publish"); + thermostat_humidity_sensor_->publish_state(thermostat_humidity_sensor_->raw_state); + } if (compressor_frequency_sensor_ && (compressor_frequency_sensor_->raw_state != compressor_frequency_sensor_->state)) { ESP_LOGI(TAG, "Compressor frequency differs, do publish"); @@ -216,6 +221,10 @@ void MitsubishiUART::do_publish_() { ESP_LOGI(TAG, "Error code state differs, do publish"); error_code_sensor_->publish_state(error_code_sensor_->raw_state); } + if (thermostat_battery_sensor_ && (thermostat_battery_sensor_->raw_state != thermostat_battery_sensor_->state)) { + ESP_LOGI(TAG, "Thermostat battery state differs, do publish"); + thermostat_battery_sensor_->publish_state(thermostat_battery_sensor_->raw_state); + } // Binary sensors automatically dedup publishes (I think) and so will only actually publish on change filter_status_sensor_->publish_state(filter_status_sensor_->state); diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.h b/esphome/components/mitsubishi_itp/mitsubishi_uart.h index b20057185d0c..9b145a9426db 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.h +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.h @@ -4,6 +4,7 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" #include "esphome/components/uart/uart.h" +#include "esphome/components/time/real_time_clock.h" #include "esphome/components/climate/climate.h" #include "esphome/components/select/select.h" #include "esphome/components/sensor/sensor.h" @@ -32,6 +33,8 @@ const std::string TEMPERATURE_SOURCE_THERMOSTAT = "Thermostat"; const std::array ACTUAL_FAN_SPEED_NAMES = {"Off", "Very Low", "Low", "Medium", "High", FAN_MODE_VERYHIGH, "Quiet"}; +const std::array THERMOSTAT_BATTERY_STATE_NAMES = {"OK", "Low", "Critical", "Replace", "Unknown"}; + class MitsubishiUART : public PollingComponent, public climate::Climate, public PacketProcessor { public: /** @@ -69,6 +72,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public // Sensor setters void set_thermostat_temperature_sensor(sensor::Sensor *sensor) { thermostat_temperature_sensor_ = sensor; }; void set_outdoor_temperature_sensor(sensor::Sensor *sensor) { outdoor_temperature_sensor_ = sensor; }; + void set_thermostat_humidity_sensor(sensor::Sensor *sensor) { thermostat_humidity_sensor_ = sensor; } void set_compressor_frequency_sensor(sensor::Sensor *sensor) { compressor_frequency_sensor_ = sensor; }; void set_actual_fan_sensor(text_sensor::TextSensor *sensor) { actual_fan_sensor_ = sensor; }; void set_filter_status_sensor(binary_sensor::BinarySensor *sensor) { filter_status_sensor_ = sensor; }; @@ -77,6 +81,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void set_standby_sensor(binary_sensor::BinarySensor *sensor) { standby_sensor_ = sensor; }; void set_isee_status_sensor(binary_sensor::BinarySensor *sensor) { isee_status_sensor_ = sensor; } void set_error_code_sensor(text_sensor::TextSensor *sensor) { error_code_sensor_ = sensor; }; + void set_thermostat_battery_sensor(text_sensor::TextSensor *sensor) { thermostat_battery_sensor_ = sensor; } // Select setters void set_temperature_source_select(select::Select *select) { temperature_source_select_ = select; }; @@ -98,6 +103,8 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public // Turns on or off actively sending packets void set_active_mode(const bool active) { active_mode_ = active; }; + void set_time_source(time::RealTimeClock *rtc) { time_source = rtc; } + protected: void route_packet_(const Packet &packet); @@ -112,9 +119,17 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void process_packet(const StatusGetResponsePacket &packet) override; void process_packet(const RunStateGetResponsePacket &packet) override; void process_packet(const ErrorStateGetResponsePacket &packet) override; + void process_packet(const SettingsSetRequestPacket &packet) override; void process_packet(const RemoteTemperatureSetRequestPacket &packet) override; + void process_packet(const KumoThermostatSensorStatusPacket &packet) override; + void process_packet(const KumoThermostatHelloPacket &packet) override; + void process_packet(const KumoThermostatStateSyncPacket &packet) override; + void process_packet(const KumoAASetRequestPacket &packet) override; void process_packet(const SetResponsePacket &packet) override; + void handle_kumo_adapter_state_get_request(const GetRequestPacket &packet) override; + void handle_kumo_aa_get_request(const GetRequestPacket &packet) override; + void do_publish_(); private: @@ -161,8 +176,12 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public ESPPreferenceObject preferences_; + // Time Source + time::RealTimeClock *time_source = nullptr; + // Internal sensors sensor::Sensor *thermostat_temperature_sensor_ = nullptr; + sensor::Sensor *thermostat_humidity_sensor_ = nullptr; sensor::Sensor *compressor_frequency_sensor_ = nullptr; sensor::Sensor *outdoor_temperature_sensor_ = nullptr; text_sensor::TextSensor *actual_fan_sensor_ = nullptr; @@ -172,6 +191,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public binary_sensor::BinarySensor *standby_sensor_ = nullptr; binary_sensor::BinarySensor *isee_status_sensor_ = nullptr; text_sensor::TextSensor *error_code_sensor_ = nullptr; + text_sensor::TextSensor *thermostat_battery_sensor_ = nullptr; // Selects select::Select *temperature_source_select_; @@ -185,6 +205,9 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void send_if_active_(const Packet &packet); bool active_mode_ = true; + + float last_cool_setpoint_ = NAN; + float last_heat_setpoint_ = NAN; }; struct MUARTPreferences { diff --git a/esphome/components/mitsubishi_itp/muart_bridge.cpp b/esphome/components/mitsubishi_itp/muart_bridge.cpp index 196bfc439a2a..de41d20778c4 100644 --- a/esphome/components/mitsubishi_itp/muart_bridge.cpp +++ b/esphome/components/mitsubishi_itp/muart_bridge.cpp @@ -173,8 +173,8 @@ void MUARTBridge::classify_and_process_raw_packet_(RawPacket &pkt) const { case GetCommand::STATUS: process_raw_packet_(pkt, false); break; - case GetCommand::A_9: - process_raw_packet_(pkt, false); + case GetCommand::KUMO_GET_ADAPTER_STATE: + process_raw_packet_(pkt, false); break; default: process_raw_packet_(pkt, false); @@ -188,8 +188,17 @@ void MUARTBridge::classify_and_process_raw_packet_(RawPacket &pkt) const { case SetCommand::SETTINGS: process_raw_packet_(pkt, true); break; - case SetCommand::THERMOSTAT_HELLO: - process_raw_packet_(pkt, false); + case SetCommand::KUMO_THERMOSTAT_SENSOR_STATUS: + process_raw_packet_(pkt, true); + break; + case SetCommand::KUMO_THERMOSTAT_HELLO: + process_raw_packet_(pkt, false); + break; + case SetCommand::KUMO_THERMOSTAT_STATE_SYNC: + process_raw_packet_(pkt, true); + break; + case SetCommand::KUMO_AA: + process_raw_packet_(pkt, true); break; default: process_raw_packet_(pkt, true); diff --git a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp index 6cfcfad83a42..a3f137044bf8 100644 --- a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp +++ b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp @@ -1,6 +1,7 @@ #include "muart_packet.h" #include "muart_utils.h" #include "mitsubishi_uart.h" +#include "esphome/core/datatypes.h" namespace esphome { namespace mitsubishi_itp { @@ -65,18 +66,69 @@ std::string RemoteTemperatureSetRequestPacket::to_string() const { "\n Temp:" + std::to_string(get_remote_temperature())); } -std::string ThermostatHelloRequestPacket::to_string() const { - return ("Thermostat Hello: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Model: " + get_thermostat_model() + +std::string KumoThermostatSensorStatusPacket::to_string() const { + return ("Kumo Thermostat Sensor Status: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + + "\n Indoor RH: " + std::to_string(get_indoor_humidity_percent()) + "%" + + " MHK Battery: " + THERMOSTAT_BATTERY_STATE_NAMES[get_thermostat_battery_state()] + + "(" + std::to_string(get_thermostat_battery_state()) + ")" + + " Sensor Flags: " + std::to_string(get_sensor_flags())); +} + +std::string KumoThermostatHelloPacket::to_string() const { + return ("Kumo Thermostat Hello: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Model: " + get_thermostat_model() + " Serial: " + get_thermostat_serial() + " Version: " + get_thermostat_version_string()); } +std::string KumoThermostatStateSyncPacket::to_string() const { + uint8_t flags = get_flags(); + + std::string result = "Kumo Thermostat Sync " + Packet::to_string() + CONSOLE_COLOR_PURPLE + + "\n Flags: " + format_hex(flags) + " =>"; + + if (flags & TSSF_TIMESTAMP) { + ESPTime timestamp{}; + get_thermostat_timestamp(×tamp); + + result += " TS Time: " + timestamp.strftime("%Y-%m-%d %H:%M:%S"); + } + + if (flags & TSSF_HEAT_SETPOINT) result += " HeatSetpoint: " + std::to_string(get_heat_setpoint()); + if (flags & TSSF_COOL_SETPOINT) result += " CoolSetpoint: " + std::to_string(get_cool_setpoint()); + + return result; +} + +std::string GetRequestPacket::to_string() const { + return ("Get Request: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + + "\n CommandID: " + format_hex((uint8_t) get_requested_command())); +} + +std::string SettingsSetRequestPacket::to_string() const { + uint8_t flags = get_flags(); + uint8_t flags2 = get_flags_2(); + + std::string result = "Settings Set Request: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + + "\n Flags: " + format_hex(flags2) + format_hex(flags) + " =>"; + + if (flags & SettingFlag::SF_POWER) result += " Power: " + std::to_string(get_power()); + if (flags & SettingFlag::SF_MODE) result += " Mode: " + std::to_string(get_mode()); + if (flags & SettingFlag::SF_TARGET_TEMPERATURE) result += " TargetTemp: " + std::to_string(get_target_temp()); + if (flags & SettingFlag::SF_FAN) result += " Fan: " + std::to_string(get_fan()); + if (flags & SettingFlag::SF_VANE) result += " Vane: " + std::to_string(get_vane()); + + if (flags2 & SettingFlag2::SF2_HORIZONTAL_VANE) + result += " HVane: " + std::to_string(get_horizontal_vane()) + (get_horizontal_vane_msb() ? " (MSB Set)" : ""); + + return result; +} + // TODO: Are there function implementations for packets in the .h file? (Yes) Should they be here? // SettingsSetRequestPacket functions void SettingsSetRequestPacket::add_settings_flag_(const SettingFlag flag_to_add) { add_flag(flag_to_add); } -void SettingsSetRequestPacket::add_settings_flag2_(const SettingFlaG2 flag2_to_add) { add_flag2(flag2_to_add); } +void SettingsSetRequestPacket::add_settings_flag2_(const SettingFlag2 flag2_to_add) { add_flag2(flag2_to_add); } SettingsSetRequestPacket &SettingsSetRequestPacket::set_power(const bool is_on) { pkt_.set_payload_byte(PLINDEX_POWER, is_on ? 0x01 : 0x00); @@ -129,6 +181,17 @@ SettingsSetRequestPacket &SettingsSetRequestPacket::set_horizontal_vane(const Ho return *this; } +float SettingsSetRequestPacket::get_target_temp() const { + uint8_t enhanced_raw_temp = pkt_.get_payload_byte(PLINDEX_TARGET_TEMPERATURE); + + if (enhanced_raw_temp == 0x00) { + uint8_t legacy_raw_temp = pkt_.get_payload_byte(PLINDEX_TARGET_TEMPERATURE_CODE); + return MUARTUtils::legacy_target_temp_to_deg_c(legacy_raw_temp); + } + + return MUARTUtils::temp_scale_a_to_deg_c(enhanced_raw_temp); +} + // SettingsGetResponsePacket functions float SettingsGetResponsePacket::get_target_temp() const { uint8_t enhanced_raw_temp = pkt_.get_payload_byte(PLINDEX_TARGETTEMP); @@ -206,22 +269,71 @@ float CurrentTempGetResponsePacket::get_outdoor_temp() const { return enhanced_raw_temp == 0 ? NAN : MUARTUtils::temp_scale_a_to_deg_c(enhanced_raw_temp); } -// ThermostatHelloRequestPacket functions -std::string ThermostatHelloRequestPacket::get_thermostat_model() const { - return MUARTUtils::decode_n_bit_string((pkt_.get_bytes() + 1), 3, 6); +// KumoThermostatHelloPacket functions +std::string KumoThermostatHelloPacket::get_thermostat_model() const { + return MUARTUtils::decode_n_bit_string((pkt_.get_payload_bytes(1)), 4, 6); } -std::string ThermostatHelloRequestPacket::get_thermostat_serial() const { - return MUARTUtils::decode_n_bit_string((pkt_.get_bytes() + 4), 8, 6); +std::string KumoThermostatHelloPacket::get_thermostat_serial() const { + return MUARTUtils::decode_n_bit_string((pkt_.get_payload_bytes(4)), 12, 6); } -std::string ThermostatHelloRequestPacket::get_thermostat_version_string() const { +std::string KumoThermostatHelloPacket::get_thermostat_version_string() const { char buf[16]; sprintf(buf, "%02d.%02d.%02d", pkt_.get_payload_byte(13), pkt_.get_payload_byte(14), pkt_.get_payload_byte(15)); return buf; } +// KumoThermostatStateSyncPacket functions +int32_t KumoThermostatStateSyncPacket::get_thermostat_timestamp(esphome::ESPTime *outTimestamp) const { + int32_be_t magic; + std::memcpy(&magic, pkt_.get_payload_bytes(PLINDEX_THERMOSTAT_TIMESTAMP), 4); + + outTimestamp->second = magic & 63; + outTimestamp->minute = (magic >> 6) & 63; + outTimestamp->hour = (magic >> 12) & 31; + outTimestamp->day_of_month = (magic >> 17) & 31; + outTimestamp->month = (magic >> 22) & 15; + outTimestamp->year = (magic >> 26) + 2017; + + outTimestamp->recalc_timestamp_local(); + return outTimestamp->timestamp; +} + +float KumoThermostatStateSyncPacket::get_heat_setpoint() const { + uint8_t enhancedRawTemp = pkt_.get_payload_byte(PLINDEX_HEAT_SETPOINT); + return MUARTUtils::temp_scale_a_to_deg_c(enhancedRawTemp); +} + +float KumoThermostatStateSyncPacket::get_cool_setpoint() const { + uint8_t enhancedRawTemp = pkt_.get_payload_byte(PLINDEX_COOL_SETPOINT); + return MUARTUtils::temp_scale_a_to_deg_c(enhancedRawTemp); +} + +// KumoCloudStateSyncPacket functions +KumoCloudStateSyncPacket &KumoCloudStateSyncPacket::set_timestamp(esphome::ESPTime ts) { + int32_t encodedTimestamp = ((ts.year - 2017) << 26) | (ts.month << 22) | (ts.day_of_month << 17) | + (ts.hour << 12) | (ts.minute << 6) | (ts.second); + + int32_t swappedTimestamp = byteswap(encodedTimestamp); + + pkt_.set_payload_bytes(PLINDEX_KUMOCLOUD_TIMESTAMP, &swappedTimestamp, 4); + pkt_.set_payload_byte(10, 0x07); // ??? + + return *this; +} + +KumoCloudStateSyncPacket &KumoCloudStateSyncPacket::set_heat_setpoint(float highTemp) { + pkt_.set_payload_byte(PLINDEX_HEAT_SETPOINT, MUARTUtils::deg_c_to_temp_scale_a(highTemp)); + return *this; +} + +KumoCloudStateSyncPacket &KumoCloudStateSyncPacket::set_cool_setpoint(float lowTemp) { + pkt_.set_payload_byte(PLINDEX_COOL_SETPOINT, MUARTUtils::deg_c_to_temp_scale_a(lowTemp)); + return *this; +} + // ErrorStateGetResponsePacket functions std::string ErrorStateGetResponsePacket::get_short_code() const { const char *upper_alphabet = "AbEFJLPU"; diff --git a/esphome/components/mitsubishi_itp/muart_packet.h b/esphome/components/mitsubishi_itp/muart_packet.h index 58e2b025acf4..867332eb0c31 100644 --- a/esphome/components/mitsubishi_itp/muart_packet.h +++ b/esphome/components/mitsubishi_itp/muart_packet.h @@ -5,6 +5,7 @@ #include "esphome/components/uart/uart.h" #include "muart_rawpacket.h" #include "muart_utils.h" +#include "esphome/core/time.h" #include namespace esphome { @@ -40,7 +41,9 @@ class Packet { bool is_checksum_valid() const { return pkt_.is_checksum_valid(); }; // Returns flags (ONLY APPLICABLE FOR SOME COMMANDS) + // TODO: Probably combine these a bit? uint8_t get_flags() const { return pkt_.get_payload_byte(PLINDEX_FLAGS); } + uint8_t get_flags_2() const { return pkt_.get_payload_byte(PLINDEX_FLAGS2); } // Sets flags (ONLY APPLICABLE FOR SOME COMMANDS) void set_flags(uint8_t flag_value); // Adds a flag (ONLY APPLICABLE FOR SOME COMMANDS) @@ -173,6 +176,10 @@ class GetRequestPacket : public Packet { } using Packet::Packet; + GetCommand get_requested_command() const { return (GetCommand) pkt_.get_payload_byte(0); } + + std::string to_string() const override; + private: GetRequestPacket(GetCommand get_command) : Packet(RawPacket(PacketType::GET_REQUEST, 1)) { pkt_.set_payload_byte(0, static_cast(get_command)); @@ -283,7 +290,7 @@ class SettingsSetRequestPacket : public Packet { SF_VANE = 0x10 }; - enum SettingFlaG2 : uint8_t { + enum SettingFlag2 : uint8_t { SF2_HORIZONTAL_VANE = 0x01, }; @@ -331,6 +338,15 @@ class SettingsSetRequestPacket : public Packet { } using Packet::Packet; + uint8_t get_power() const { return pkt_.get_payload_byte(PLINDEX_POWER); } + ModeByte get_mode() const { return (ModeByte) pkt_.get_payload_byte(PLINDEX_MODE); } + FanByte get_fan() const { return (FanByte) pkt_.get_payload_byte(PLINDEX_FAN); } + VaneByte get_vane() const { return (VaneByte) pkt_.get_payload_byte(PLINDEX_VANE); } + HorizontalVaneByte get_horizontal_vane() const { return (HorizontalVaneByte) (pkt_.get_payload_byte(PLINDEX_HORIZONTAL_VANE) & 0x7F); } + bool get_horizontal_vane_msb() const { return pkt_.get_payload_byte(PLINDEX_HORIZONTAL_VANE) & 0x80; } + + float get_target_temp() const; + SettingsSetRequestPacket &set_power(bool is_on); SettingsSetRequestPacket &set_mode(ModeByte mode); SettingsSetRequestPacket &set_target_temperature(float temperature_degress_c); @@ -338,9 +354,11 @@ class SettingsSetRequestPacket : public Packet { SettingsSetRequestPacket &set_vane(VaneByte vane); SettingsSetRequestPacket &set_horizontal_vane(HorizontalVaneByte horizontal_vane); + std::string to_string() const override; + private: void add_settings_flag_(SettingFlag flag_to_add); - void add_settings_flag2_(SettingFlaG2 flag2_to_add); + void add_settings_flag2_(SettingFlag2 flag2_to_add); }; class RemoteTemperatureSetRequestPacket : public Packet { @@ -386,13 +404,36 @@ class SetRunStatePacket : public Packet { SetRunStatePacket &set_filter_reset(bool do_reset); }; +class KumoThermostatSensorStatusPacket : public Packet { + using Packet::Packet; + + public: + enum ThermostatBatteryState : uint8_t { + THERMOSTAT_BATTERY_OK = 0x00, + THERMOSTAT_BATTERY_LOW = 0x01, + THERMOSTAT_BATTERY_CRITICAL = 0x02, + THERMOSTAT_BATTERY_REPLACE = 0x03, + THERMOSTAT_BATTERY_UNKNOWN = 0x04, + }; + + KumoThermostatSensorStatusPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_THERMOSTAT_SENSOR_STATUS)); + } + + uint8_t get_indoor_humidity_percent() const { return pkt_.get_payload_byte(5); } + ThermostatBatteryState get_thermostat_battery_state() const { return (ThermostatBatteryState) pkt_.get_payload_byte(6); } + uint8_t get_sensor_flags() const { return pkt_.get_payload_byte(7); } + + std::string to_string() const override; +}; + // Sent by MHK2 but with no response; defined to allow setResponseExpected(false) -class ThermostatHelloRequestPacket : public Packet { +class KumoThermostatHelloPacket : public Packet { using Packet::Packet; public: - ThermostatHelloRequestPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 4)) { - pkt_.set_payload_byte(0, static_cast(SetCommand::THERMOSTAT_HELLO)); + KumoThermostatHelloPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_THERMOSTAT_HELLO)); } std::string get_thermostat_model() const; @@ -402,13 +443,66 @@ class ThermostatHelloRequestPacket : public Packet { std::string to_string() const override; }; -// Sent by MHK2 but with no response; defined to allow setResponseExpected(false) -class A9GetRequestPacket : public Packet { +class KumoThermostatStateSyncPacket : public Packet { + // Packet 0x41 - AG 0xA8 + + static const uint8_t PLINDEX_THERMOSTAT_TIMESTAMP = 2; + static const uint8_t PLINDEX_HEAT_SETPOINT = 8; + static const uint8_t PLINDEX_COOL_SETPOINT = 9; + + enum TSStateSyncFlags : uint8_t { + TSSF_TIMESTAMP = 0x01, + TSSF_HEAT_SETPOINT = 0x08, + TSSF_COOL_SETPOINT = 0x10, + }; + using Packet::Packet; public: - A9GetRequestPacket() : Packet(RawPacket(PacketType::GET_REQUEST, 10)) { - pkt_.set_payload_byte(0, static_cast(GetCommand::A_9)); + KumoThermostatStateSyncPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_THERMOSTAT_STATE_SYNC)); + } + + int32_t get_thermostat_timestamp(ESPTime* outTimestamp) const; + float get_heat_setpoint() const; + float get_cool_setpoint() const; + + std::string to_string() const override; +}; + +class KumoCloudStateSyncPacket : public Packet { + static const uint8_t PLINDEX_KUMOCLOUD_TIMESTAMP = 1; + static const uint8_t PLINDEX_HEAT_SETPOINT = 7; + static const uint8_t PLINDEX_COOL_SETPOINT = 8; + + using Packet::Packet; + + public: + KumoCloudStateSyncPacket() : Packet(RawPacket(PacketType::GET_RESPONSE, 16)) { + pkt_.set_payload_byte(0, static_cast(GetCommand::KUMO_GET_ADAPTER_STATE)); + } + + KumoCloudStateSyncPacket &set_timestamp(ESPTime ts); + KumoCloudStateSyncPacket &set_heat_setpoint(float highTemp); + KumoCloudStateSyncPacket &set_cool_setpoint(float lowTemp); +}; + +class KumoAASetRequestPacket : public Packet { + using Packet::Packet; + + public: + KumoAASetRequestPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_AA)); + } +}; + +class KumoABGetRequestPacket : public Packet { + using Packet::Packet; + + public: + KumoABGetRequestPacket() : Packet(RawPacket(PacketType::GET_RESPONSE, 16)) { + pkt_.set_payload_byte(0, static_cast(GetCommand::KUMO_AB)); + pkt_.set_payload_byte(1, 1); } }; @@ -425,8 +519,18 @@ class PacketProcessor { virtual void process_packet(const StatusGetResponsePacket &packet){}; virtual void process_packet(const RunStateGetResponsePacket &packet){}; virtual void process_packet(const ErrorStateGetResponsePacket &packet){}; + virtual void process_packet(const SettingsSetRequestPacket &packet){}; virtual void process_packet(const RemoteTemperatureSetRequestPacket &packet){}; + virtual void process_packet(const KumoThermostatSensorStatusPacket &packet){}; + virtual void process_packet(const KumoThermostatHelloPacket &packet){}; + virtual void process_packet(const KumoThermostatStateSyncPacket &packet){}; + virtual void process_packet(const KumoCloudStateSyncPacket &packet){}; + virtual void process_packet(const KumoAASetRequestPacket &packet){}; + virtual void process_packet(const KumoABGetRequestPacket &packet){}; virtual void process_packet(const SetResponsePacket &packet){}; + + virtual void handle_kumo_adapter_state_get_request(const GetRequestPacket &packet){}; + virtual void handle_kumo_aa_get_request(const GetRequestPacket &packet){}; }; } // namespace mitsubishi_itp diff --git a/esphome/components/mitsubishi_itp/muart_rawpacket.cpp b/esphome/components/mitsubishi_itp/muart_rawpacket.cpp index cc0bf0859e21..dc9223cb11b2 100644 --- a/esphome/components/mitsubishi_itp/muart_rawpacket.cpp +++ b/esphome/components/mitsubishi_itp/muart_rawpacket.cpp @@ -60,5 +60,11 @@ RawPacket &RawPacket::set_payload_byte(const uint8_t payload_byte_index, const u return *this; } +RawPacket &RawPacket::set_payload_bytes(const uint8_t begin_index, const void* value, const size_t size) { + memcpy(&packet_bytes_[PACKET_HEADER_SIZE + begin_index], value, size); + update_checksum_(); + return *this; +} + } // namespace mitsubishi_itp } // namespace esphome diff --git a/esphome/components/mitsubishi_itp/muart_rawpacket.h b/esphome/components/mitsubishi_itp/muart_rawpacket.h index b7cf47771d9f..0cf3c8389558 100644 --- a/esphome/components/mitsubishi_itp/muart_rawpacket.h +++ b/esphome/components/mitsubishi_itp/muart_rawpacket.h @@ -35,7 +35,8 @@ enum class GetCommand : uint8_t { ERROR_INFO = 0x04, STATUS = 0x06, RUN_STATE = 0x09, - A_9 = 0xa9 + KUMO_GET_ADAPTER_STATE = 0xa9, + KUMO_AB = 0xab, }; // Used to specify certain packet subtypes @@ -43,7 +44,10 @@ enum class SetCommand : uint8_t { SETTINGS = 0x01, REMOTE_TEMPERATURE = 0x07, RUN_STATE = 0x08, - THERMOSTAT_HELLO = 0xa7 + KUMO_THERMOSTAT_SENSOR_STATUS = 0xa6, + KUMO_THERMOSTAT_HELLO = 0xa7, + KUMO_THERMOSTAT_STATE_SYNC = 0xa8, + KUMO_AA = 0xaa, }; // Which MUARTBridge was the packet read from (used to determine flow direction of the packet) @@ -92,9 +96,11 @@ class RawPacket { ControllerAssociation get_controller_association() const { return controller_association_; }; RawPacket &set_payload_byte(uint8_t payload_byte_index, uint8_t value); + RawPacket &set_payload_bytes(uint8_t begin_index, const void *value, size_t size); uint8_t get_payload_byte(const uint8_t payload_byte_index) const { return packet_bytes_[PACKET_HEADER_SIZE + payload_byte_index]; }; + const uint8_t *get_payload_bytes(size_t startIndex = 0) const { return &packet_bytes_[PACKET_HEADER_SIZE + startIndex]; } private: static const int PLINDEX_COMMAND = 0; diff --git a/esphome/components/mitsubishi_itp/muart_utils.h b/esphome/components/mitsubishi_itp/muart_utils.h index f929106abd18..759f6a5059cb 100644 --- a/esphome/components/mitsubishi_itp/muart_utils.h +++ b/esphome/components/mitsubishi_itp/muart_utils.h @@ -7,11 +7,10 @@ class MUARTUtils { public: /// Read a string out of data, wordSize bits at a time. /// Used to decode serial numbers and other information from a thermostat. - static std::string decode_n_bit_string(const uint8_t data[], size_t data_length, size_t word_size) { - auto result_length = (data_length / word_size) + (data_length % word_size != 0); + static std::string decode_n_bit_string(const uint8_t data[], size_t data_length, size_t word_size = 6) { auto result = std::string(); - for (int i = 0; i < result_length; i++) { + for (int i = 0; i < data_length; i++) { auto bits = bit_slice(data, i * word_size, ((i + 1) * word_size) - 1); if (bits <= 0x1F) bits += 0x40; @@ -60,16 +59,27 @@ class MUARTUtils { private: /// Extract the specified bits (inclusive) from an arbitrarily-sized byte array. Does not perform bounds checks. + /// Max extraction is 64 bits. Preserves endianness of incoming data stream. static uint64_t bit_slice(const uint8_t ds[], size_t start, size_t end) { - // Lazies! https://stackoverflow.com/a/25297870/1817097 - uint64_t s = 0; - size_t i, n = (end - 1) / 8; - for (i = 0; i <= n; ++i) - s = (s << 8) + ds[i]; - s >>= (n + 1) * 8 - end; - uint64_t mask = (((uint64_t) 1) << (end - start + 1)) - 1; // len = end - start + 1 - s &= mask; - return s; + if ((end - start) >= 64) + return 0; + + uint64_t result = 0; + + size_t start_byte = (start) / 8; + size_t end_byte = ((end) / 8) + 1; // exclusive, used for length calc + + // raw copy the relevant bytes into our int64, preserving endian-ness + std::memcpy(&result, &ds[start_byte], end_byte - start_byte); + result = byteswap(result); + + // shift out the bits we don't want from the end (64 + credit any pre-sliced bits) + result >>= (sizeof(uint64_t) * 8) + (start_byte * 8) - end - 1; + + // mask out the number of bits we want + result &= (1 << (end - start + 1)) - 1; + + return result; } }; From 75dc926e46dee5d28aa8651abb1c972ce487e51e Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 19 Jun 2024 11:08:31 -0700 Subject: [PATCH 02/15] Add toggle for KEM --- esphome/components/mitsubishi_itp/climate.py | 5 +++++ .../mitsubishi_uart-packetprocessing.cpp | 12 ++++++++++++ .../components/mitsubishi_itp/mitsubishi_uart.cpp | 4 ++++ esphome/components/mitsubishi_itp/mitsubishi_uart.h | 8 ++++++++ 4 files changed, 29 insertions(+) diff --git a/esphome/components/mitsubishi_itp/climate.py b/esphome/components/mitsubishi_itp/climate.py index 1ec41ea55aab..2bf290f139a0 100644 --- a/esphome/components/mitsubishi_itp/climate.py +++ b/esphome/components/mitsubishi_itp/climate.py @@ -74,6 +74,7 @@ ) CONF_DISABLE_ACTIVE_MODE = "disable_active_mode" +CONF_ENABLE_KUMO_EMULATION = "kumo_emulation" # EXPERIMENTAL FEATURE - Enables Kumo packet handling. DEFAULT_POLLING_INTERVAL = "5s" @@ -135,6 +136,7 @@ cv.use_id(sensor.Sensor) ), cv.Optional(CONF_DISABLE_ACTIVE_MODE, default=False): cv.boolean, + cv.Optional(CONF_ENABLE_KUMO_EMULATION, default=False): cv.boolean, } ).extend(cv.polling_component_schema(DEFAULT_POLLING_INTERVAL)) @@ -420,3 +422,6 @@ async def to_code(config): # Debug Settings if dam_conf := config.get(CONF_DISABLE_ACTIVE_MODE): cg.add(getattr(muart_component, "set_active_mode")(not dam_conf)) + + if kumo_emulation := config.get(CONF_ENABLE_KUMO_EMULATION): + cg.add(getattr(muart_component, "set_kumo_emulation_mode")(kumo_emulation)) diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index 251f0892c87d..0e13aa6d2daf 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -384,6 +384,8 @@ void MitsubishiUART::process_packet(const RemoteTemperatureSetRequestPacket &pac } void MitsubishiUART::process_packet(const KumoThermostatSensorStatusPacket &packet) { + if (!kumo_emulation_mode_) return; + ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); if (thermostat_humidity_sensor_ && packet.get_flags() & 0x04) { @@ -402,12 +404,16 @@ void MitsubishiUART::process_packet(const KumoThermostatSensorStatusPacket &pack } void MitsubishiUART::process_packet(const KumoThermostatHelloPacket &packet) { + if (!kumo_emulation_mode_) return; + ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); ts_bridge_->send_packet(SetResponsePacket()); } void MitsubishiUART::process_packet(const KumoThermostatStateSyncPacket &packet) { + if (!kumo_emulation_mode_) return; + ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); if (packet.get_flags() & 0x08) this->last_heat_setpoint_ = packet.get_heat_setpoint(); @@ -417,6 +423,8 @@ void MitsubishiUART::process_packet(const KumoThermostatStateSyncPacket &packet) } void MitsubishiUART::process_packet(const KumoAASetRequestPacket &packet) { + if (!kumo_emulation_mode_) return; + ESP_LOGV(TAG, "Processing inbound KumoAASetRequestPacket: %s", packet.to_string().c_str()); ts_bridge_->send_packet(SetResponsePacket()); @@ -430,6 +438,8 @@ void MitsubishiUART::process_packet(const SetResponsePacket &packet) { // Process incoming data requests (Kumo) void MitsubishiUART::handle_kumo_adapter_state_get_request(const GetRequestPacket &packet) { + if (!kumo_emulation_mode_) return; + auto response = KumoCloudStateSyncPacket(); response.set_heat_setpoint(this->last_heat_setpoint_); @@ -446,6 +456,8 @@ void MitsubishiUART::handle_kumo_adapter_state_get_request(const GetRequestPacke } void MitsubishiUART::handle_kumo_aa_get_request(const GetRequestPacket &packet) { + if (!kumo_emulation_mode_) return; + auto response = KumoABGetRequestPacket(); ts_bridge_->send_packet(response); diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp index 4558dcbd35c7..1d8bcddb41af 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp @@ -114,6 +114,10 @@ void MitsubishiUART::dump_config() { if (capabilities_cache_.has_value()) { ESP_LOGCONFIG(TAG, "Discovered Capabilities: %s", capabilities_cache_.value().to_string().c_str()); } + + if (kumo_emulation_mode_) { + ESP_LOGCONFIG(TAG, "Kumo Emulation Mode is ENABLED! This is an *experimental mode* and things may break."); + } } // Set thermostat UART component diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.h b/esphome/components/mitsubishi_itp/mitsubishi_uart.h index 9b145a9426db..f49eda576441 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.h +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.h @@ -103,6 +103,9 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public // Turns on or off actively sending packets void set_active_mode(const bool active) { active_mode_ = active; }; + // Turns on or off Kumo emulation mode + void set_kumo_emulation_mode(const bool mode) { kumo_emulation_mode_ = mode; } + void set_time_source(time::RealTimeClock *rtc) { time_source = rtc; } protected: @@ -206,6 +209,11 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void send_if_active_(const Packet &packet); bool active_mode_ = true; + bool kumo_emulation_mode_ = false; + + // used to track heat/cool setpoints for parity sync with MHK units. + // necessary to not clobber the union of setpoints, since ESPHome doesnt gracefully handle simultaneous cool and + // heat setpoints being set. float last_cool_setpoint_ = NAN; float last_heat_setpoint_ = NAN; }; From e2e6ef0cbc86347d4a531ca2127c10eabb83916d Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 19 Jun 2024 13:56:59 -0700 Subject: [PATCH 03/15] Add Identify 0xCD - Rename extended connect to Identify --- .../mitsubishi_uart-packetprocessing.cpp | 4 +- .../mitsubishi_itp/mitsubishi_uart.cpp | 2 +- .../mitsubishi_itp/mitsubishi_uart.h | 6 +-- .../mitsubishi_itp/muart_bridge.cpp | 8 ++-- .../mitsubishi_itp/muart_packet-derived.cpp | 13 ++++--- .../components/mitsubishi_itp/muart_packet.h | 37 +++++++++++++++---- .../mitsubishi_itp/muart_rawpacket.h | 4 +- 7 files changed, 49 insertions(+), 25 deletions(-) diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index 0e13aa6d2daf..bf4acf08e17c 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -36,13 +36,13 @@ void MitsubishiUART::process_packet(const ConnectResponsePacket &packet) { ESP_LOGI(TAG, "Heatpump connected."); }; -void MitsubishiUART::process_packet(const ExtendedConnectRequestPacket &packet) { +void MitsubishiUART::process_packet(const BaseCapabilitiesRequestPacket &packet) { // Nothing to be done for these except forward them along from thermostat to heat pump. // This method defined so that these packets are not "unhandled" ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); route_packet_(packet); }; -void MitsubishiUART::process_packet(const ExtendedConnectResponsePacket &packet) { +void MitsubishiUART::process_packet(const BaseCapabilitiesResponsePacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); route_packet_(packet); // Not sure if there's any needed content in this response, so assume we're connected. diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp index 1d8bcddb41af..6db1d8549710 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp @@ -150,7 +150,7 @@ void MitsubishiUART::update() { // autoconf. // For now, just requesting it as part of our "init loops" is a good first step. if (!this->capabilities_requested_) { - IFACTIVE(hp_bridge_.send_packet(ExtendedConnectRequestPacket::instance()); this->capabilities_requested_ = true;) + IFACTIVE(hp_bridge_.send_packet(BaseCapabilitiesRequestPacket::instance()); this->capabilities_requested_ = true;) } // Before requesting additional updates, publish any changes waiting from packets received diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.h b/esphome/components/mitsubishi_itp/mitsubishi_uart.h index f49eda576441..39ee2a92d339 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.h +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.h @@ -114,8 +114,8 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void process_packet(const Packet &packet) override; void process_packet(const ConnectRequestPacket &packet) override; void process_packet(const ConnectResponsePacket &packet) override; - void process_packet(const ExtendedConnectRequestPacket &packet) override; - void process_packet(const ExtendedConnectResponsePacket &packet) override; + void process_packet(const BaseCapabilitiesRequestPacket &packet) override; + void process_packet(const BaseCapabilitiesResponsePacket &packet) override; void process_packet(const GetRequestPacket &packet) override; void process_packet(const SettingsGetResponsePacket &packet) override; void process_packet(const CurrentTempGetResponsePacket &packet) override; @@ -168,7 +168,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public // Number of times update() has been called in discovery mode size_t discovery_updates_ = 0; - optional capabilities_cache_; + optional capabilities_cache_; bool capabilities_requested_ = false; // Have we received at least one RunState response? bool run_state_received_ = false; diff --git a/esphome/components/mitsubishi_itp/muart_bridge.cpp b/esphome/components/mitsubishi_itp/muart_bridge.cpp index de41d20778c4..5843a4fc70fb 100644 --- a/esphome/components/mitsubishi_itp/muart_bridge.cpp +++ b/esphome/components/mitsubishi_itp/muart_bridge.cpp @@ -146,11 +146,11 @@ void MUARTBridge::classify_and_process_raw_packet_(RawPacket &pkt) const { process_raw_packet_(pkt, false); break; - case PacketType::EXTENDED_CONNECT_REQUEST: - process_raw_packet_(pkt, true); + case PacketType::IDENTIFY_REQUEST: + process_raw_packet_(pkt, true); break; - case PacketType::EXTENDED_CONNECT_RESPONSE: - process_raw_packet_(pkt, false); + case PacketType::IDENTIFY_RESPONSE: + process_raw_packet_(pkt, false); break; case PacketType::GET_REQUEST: diff --git a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp index a3f137044bf8..86a54ba1116e 100644 --- a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp +++ b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp @@ -10,9 +10,9 @@ namespace mitsubishi_itp { std::string ConnectRequestPacket::to_string() const { return ("Connect Request: " + Packet::to_string()); } std::string ConnectResponsePacket::to_string() const { return ("Connect Response: " + Packet::to_string()); } -std::string ExtendedConnectResponsePacket::to_string() const { +std::string BaseCapabilitiesResponsePacket::to_string() const { return ( - "Extended Connect Response: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + + "Identify Base Capabilities Response: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n HeatDisabled:" + (is_heat_disabled() ? "Yes" : "No") + " SupportsVane:" + (supports_vane() ? "Yes" : "No") + " SupportsVaneSwing:" + (supports_vane_swing() ? "Yes" : "No") @@ -29,6 +29,9 @@ std::string ExtendedConnectResponsePacket::to_string() const { "/" + std::to_string(get_max_heating_setpoint()) + " AutoSetpoint:" + std::to_string(get_min_auto_setpoint()) + "/" + std::to_string(get_max_auto_setpoint()) + " FanSpeeds:" + std::to_string(get_supported_fan_speeds())); } +std::string IdentifyCDResponsePacket::to_string() const { + return "Identify CD Response: " + Packet::to_string(); +} std::string CurrentTempGetResponsePacket::to_string() const { return ("Current Temp Response: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Temp:" + std::to_string(get_current_temp()) + @@ -350,8 +353,8 @@ std::string ErrorStateGetResponsePacket::get_short_code() const { return {upper_alphabet[(error_code & 0xE0) >> 5], lower_alphabet[low_bits]}; } -// ExtendedConnectResponsePacket functions -uint8_t ExtendedConnectResponsePacket::get_supported_fan_speeds() const { +// BaseCapabilitiesResponsePacket functions +uint8_t BaseCapabilitiesResponsePacket::get_supported_fan_speeds() const { uint8_t raw_value = ((pkt_.get_payload_byte(7) & 0x10) >> 2) + ((pkt_.get_payload_byte(8) & 0x08) >> 2) + ((pkt_.get_payload_byte(9) & 0x02) >> 1); @@ -371,7 +374,7 @@ uint8_t ExtendedConnectResponsePacket::get_supported_fan_speeds() const { } } -climate::ClimateTraits ExtendedConnectResponsePacket::as_traits() const { +climate::ClimateTraits BaseCapabilitiesResponsePacket::as_traits() const { auto ct = climate::ClimateTraits(); // always enabled diff --git a/esphome/components/mitsubishi_itp/muart_packet.h b/esphome/components/mitsubishi_itp/muart_packet.h index 867332eb0c31..865a6ce0255f 100644 --- a/esphome/components/mitsubishi_itp/muart_packet.h +++ b/esphome/components/mitsubishi_itp/muart_packet.h @@ -91,23 +91,23 @@ class ConnectResponsePacket : public Packet { }; //// -// Extended Connect +// Identify packets //// -class ExtendedConnectRequestPacket : public Packet { +class BaseCapabilitiesRequestPacket : public Packet { public: - static ExtendedConnectRequestPacket &instance() { - static ExtendedConnectRequestPacket instance; + static BaseCapabilitiesRequestPacket &instance() { + static BaseCapabilitiesRequestPacket instance; return instance; } using Packet::Packet; private: - ExtendedConnectRequestPacket() : Packet(RawPacket(PacketType::EXTENDED_CONNECT_REQUEST, 1)) { + BaseCapabilitiesRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { pkt_.set_payload_byte(0, 0xc9); } }; -class ExtendedConnectResponsePacket : public Packet { +class BaseCapabilitiesResponsePacket : public Packet { using Packet::Packet; public: @@ -149,6 +149,27 @@ class ExtendedConnectResponsePacket : public Packet { std::string to_string() const override; }; +class IdentifyCDRequestPacket : public Packet { + public: + static IdentifyCDRequestPacket &instance() { + static IdentifyCDRequestPacket instance; + return instance; + } + using Packet::Packet; + + private: + IdentifyCDRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { + pkt_.set_payload_byte(0, 0xCD); + } +}; + +class IdentifyCDResponsePacket : public Packet { + using Packet::Packet; + + public: + std::string to_string() const override; +}; + //// // Get //// @@ -511,8 +532,8 @@ class PacketProcessor { virtual void process_packet(const Packet &packet){}; virtual void process_packet(const ConnectRequestPacket &packet){}; virtual void process_packet(const ConnectResponsePacket &packet){}; - virtual void process_packet(const ExtendedConnectRequestPacket &packet){}; - virtual void process_packet(const ExtendedConnectResponsePacket &packet){}; + virtual void process_packet(const BaseCapabilitiesRequestPacket &packet){}; + virtual void process_packet(const BaseCapabilitiesResponsePacket &packet){}; virtual void process_packet(const GetRequestPacket &packet){}; virtual void process_packet(const SettingsGetResponsePacket &packet){}; virtual void process_packet(const CurrentTempGetResponsePacket &packet){}; diff --git a/esphome/components/mitsubishi_itp/muart_rawpacket.h b/esphome/components/mitsubishi_itp/muart_rawpacket.h index 0cf3c8389558..23cd9e32ded0 100644 --- a/esphome/components/mitsubishi_itp/muart_rawpacket.h +++ b/esphome/components/mitsubishi_itp/muart_rawpacket.h @@ -24,8 +24,8 @@ enum class PacketType : uint8_t { GET_RESPONSE = 0x62, SET_REQUEST = 0x41, SET_RESPONSE = 0x61, - EXTENDED_CONNECT_REQUEST = 0x5b, - EXTENDED_CONNECT_RESPONSE = 0x7b + IDENTIFY_REQUEST = 0x5b, + IDENTIFY_RESPONSE = 0x7b }; // Used to specify certain packet subtypes From cc8142b3865f98aa51e44d7980e8a0e5da031077 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 19 Jun 2024 14:30:33 -0700 Subject: [PATCH 04/15] fix typos --- .../mitsubishi_itp/muart_packet-derived.cpp | 26 +++++++++---------- .../components/mitsubishi_itp/muart_packet.h | 6 ++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp index 86a54ba1116e..f7c84ad91a56 100644 --- a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp +++ b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp @@ -145,23 +145,23 @@ SettingsSetRequestPacket &SettingsSetRequestPacket::set_mode(const ModeByte mode return *this; } -SettingsSetRequestPacket &SettingsSetRequestPacket::set_target_temperature(const float temperature_degress_c) { - if (temperature_degress_c < 63.5 && temperature_degress_c > -64.0) { - pkt_.set_payload_byte(PLINDEX_TARGET_TEMPERATURE, MUARTUtils::deg_c_to_temp_scale_a(temperature_degress_c)); +SettingsSetRequestPacket &SettingsSetRequestPacket::set_target_temperature(const float temperature_degrees_c) { + if (temperature_degrees_c < 63.5 && temperature_degrees_c > -64.0) { + pkt_.set_payload_byte(PLINDEX_TARGET_TEMPERATURE, MUARTUtils::deg_c_to_temp_scale_a(temperature_degrees_c)); pkt_.set_payload_byte(PLINDEX_TARGET_TEMPERATURE_CODE, - MUARTUtils::deg_c_to_legacy_target_temp(temperature_degress_c)); + MUARTUtils::deg_c_to_legacy_target_temp(temperature_degrees_c)); // TODO: while spawning a warning here is fine, we should (a) only actually send that warning if the system can't // support this setpoint, and (b) clamp the setpoint to the known-acceptable values. // The utility class will already clamp this for us, so we only need to worry about the warning. - if (temperature_degress_c < 16 || temperature_degress_c > 31.5) { + if (temperature_degrees_c < 16 || temperature_degrees_c > 31.5) { ESP_LOGW(PTAG, "Target temp %f is out of range for the legacy temp scale. This may be a problem on older units.", - temperature_degress_c); + temperature_degrees_c); } add_settings_flag_(SF_TARGET_TEMPERATURE); } else { - ESP_LOGW(PTAG, "Target temp %f is outside valid range - target temperature not set!", temperature_degress_c); + ESP_LOGW(PTAG, "Target temp %f is outside valid range - target temperature not set!", temperature_degrees_c); } return *this; @@ -229,14 +229,14 @@ float RemoteTemperatureSetRequestPacket::get_remote_temperature() const { } RemoteTemperatureSetRequestPacket &RemoteTemperatureSetRequestPacket::set_remote_temperature( - float temperature_degress_c) { - if (temperature_degress_c < 63.5 && temperature_degress_c > -64.0) { - pkt_.set_payload_byte(PLINDEX_REMOTE_TEMPERATURE, MUARTUtils::deg_c_to_temp_scale_a(temperature_degress_c)); + float temperature_degrees_c) { + if (temperature_degrees_c < 63.5 && temperature_degrees_c > -64.0) { + pkt_.set_payload_byte(PLINDEX_REMOTE_TEMPERATURE, MUARTUtils::deg_c_to_temp_scale_a(temperature_degrees_c)); pkt_.set_payload_byte(PLINDEX_LEGACY_REMOTE_TEMPERATURE, - MUARTUtils::deg_c_to_legacy_room_temp(temperature_degress_c)); + MUARTUtils::deg_c_to_legacy_room_temp(temperature_degrees_c)); set_flags(0x01); // Set flags to say we're providing the temperature } else { - ESP_LOGW(PTAG, "Remote temp %f is outside valid range.", temperature_degress_c); + ESP_LOGW(PTAG, "Remote temp %f is outside valid range.", temperature_degrees_c); } return *this; } @@ -245,7 +245,7 @@ RemoteTemperatureSetRequestPacket &RemoteTemperatureSetRequestPacket::use_intern return *this; } -// SettingsSetRunStatisPacket functions +// SettingsSetRunStatusPacket functions SetRunStatePacket &SetRunStatePacket::set_filter_reset(bool do_reset) { pkt_.set_payload_byte(PLINDEX_FILTER_RESET, do_reset ? 1 : 0); set_flags(0x01); diff --git a/esphome/components/mitsubishi_itp/muart_packet.h b/esphome/components/mitsubishi_itp/muart_packet.h index 865a6ce0255f..f1cf50ab1b2b 100644 --- a/esphome/components/mitsubishi_itp/muart_packet.h +++ b/esphome/components/mitsubishi_itp/muart_packet.h @@ -27,7 +27,7 @@ class Packet { Packet(RawPacket &&pkt) : pkt_(pkt){}; // TODO: Confirm this needs std::move if call to constructor ALSO has move Packet(); // For optional<> construction - // Returns a (more) human readable string of the packet + // Returns a (more) human-readable string of the packet virtual std::string to_string() const; // Is a response packet expected when this packet is sent. Defaults to true since @@ -370,7 +370,7 @@ class SettingsSetRequestPacket : public Packet { SettingsSetRequestPacket &set_power(bool is_on); SettingsSetRequestPacket &set_mode(ModeByte mode); - SettingsSetRequestPacket &set_target_temperature(float temperature_degress_c); + SettingsSetRequestPacket &set_target_temperature(float temperature_degrees_c); SettingsSetRequestPacket &set_fan(FanByte fan); SettingsSetRequestPacket &set_vane(VaneByte vane); SettingsSetRequestPacket &set_horizontal_vane(HorizontalVaneByte horizontal_vane); @@ -394,7 +394,7 @@ class RemoteTemperatureSetRequestPacket : public Packet { float get_remote_temperature() const; - RemoteTemperatureSetRequestPacket &set_remote_temperature(float temperature_degress_c); + RemoteTemperatureSetRequestPacket &set_remote_temperature(float temperature_degrees_c); RemoteTemperatureSetRequestPacket &use_internal_temperature(); std::string to_string() const override; From 08c909163ef96bab2b3c93b88f7bf433639cc7f5 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 19 Jun 2024 18:37:08 -0700 Subject: [PATCH 05/15] Only require timesource if thermostat and kumo emulation mode are set. --- esphome/components/mitsubishi_itp/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/mitsubishi_itp/climate.py b/esphome/components/mitsubishi_itp/climate.py index 2bf290f139a0..ee89b3920599 100644 --- a/esphome/components/mitsubishi_itp/climate.py +++ b/esphome/components/mitsubishi_itp/climate.py @@ -339,8 +339,8 @@ async def to_code(config): if CONF_TIME_SOURCE in config: rtc_component = await cg.get_variable(config[CONF_TIME_SOURCE]) cg.add(getattr(muart_component, "set_time_source")(rtc_component)) - elif CONF_UART_THERMOSTAT in config: - raise cv.RequiredFieldInvalid(f"{CONF_TIME_SOURCE} is required if {CONF_TS_UART} is set.") + elif CONF_UART_THERMOSTAT in config and not config.get(CONF_ENABLE_KUMO_EMULATION): + raise cv.RequiredFieldInvalid(f"{CONF_TIME_SOURCE} is required if {CONF_ENABLE_KUMO_EMULATION} is set.") # Traits traits = muart_component.config_traits() From 5ce00109296f9d9cca8297217280e3fe2b1a5057 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 19 Jun 2024 21:51:03 -0700 Subject: [PATCH 06/15] Code Review comments - Remove Kumo references almost everywhere - Route packet normally when enhanced protocol mode is disabled - Fix logic error in schema validation for time source - Rename a few things - More gracefully handle NaN values --- esphome/components/mitsubishi_itp/climate.py | 12 +-- .../mitsubishi_uart-packetprocessing.cpp | 68 ++++++++++------- .../mitsubishi_itp/mitsubishi_uart.cpp | 6 +- .../mitsubishi_itp/mitsubishi_uart.h | 27 +++---- .../mitsubishi_itp/muart_bridge.cpp | 24 +++--- .../mitsubishi_itp/muart_packet-derived.cpp | 54 +++++++------- .../components/mitsubishi_itp/muart_packet.h | 74 +++++++++---------- .../mitsubishi_itp/muart_rawpacket.h | 12 +-- 8 files changed, 150 insertions(+), 127 deletions(-) diff --git a/esphome/components/mitsubishi_itp/climate.py b/esphome/components/mitsubishi_itp/climate.py index ee89b3920599..36796b7b2223 100644 --- a/esphome/components/mitsubishi_itp/climate.py +++ b/esphome/components/mitsubishi_itp/climate.py @@ -74,7 +74,7 @@ ) CONF_DISABLE_ACTIVE_MODE = "disable_active_mode" -CONF_ENABLE_KUMO_EMULATION = "kumo_emulation" # EXPERIMENTAL FEATURE - Enables Kumo packet handling. +CONF_ENHANCED_MHK_SUPPORT = "enhanced_mhk" # EXPERIMENTAL. Will be set to default eventually. DEFAULT_POLLING_INTERVAL = "5s" @@ -136,7 +136,7 @@ cv.use_id(sensor.Sensor) ), cv.Optional(CONF_DISABLE_ACTIVE_MODE, default=False): cv.boolean, - cv.Optional(CONF_ENABLE_KUMO_EMULATION, default=False): cv.boolean, + cv.Optional(CONF_ENHANCED_MHK_SUPPORT, default=False): cv.boolean, } ).extend(cv.polling_component_schema(DEFAULT_POLLING_INTERVAL)) @@ -339,8 +339,8 @@ async def to_code(config): if CONF_TIME_SOURCE in config: rtc_component = await cg.get_variable(config[CONF_TIME_SOURCE]) cg.add(getattr(muart_component, "set_time_source")(rtc_component)) - elif CONF_UART_THERMOSTAT in config and not config.get(CONF_ENABLE_KUMO_EMULATION): - raise cv.RequiredFieldInvalid(f"{CONF_TIME_SOURCE} is required if {CONF_ENABLE_KUMO_EMULATION} is set.") + elif CONF_UART_THERMOSTAT in config and config.get(CONF_ENHANCED_MHK_SUPPORT): + raise cv.RequiredFieldInvalid(f"{CONF_TIME_SOURCE} is required if {CONF_ENHANCED_MHK_SUPPORT} is set.") # Traits traits = muart_component.config_traits() @@ -423,5 +423,5 @@ async def to_code(config): if dam_conf := config.get(CONF_DISABLE_ACTIVE_MODE): cg.add(getattr(muart_component, "set_active_mode")(not dam_conf)) - if kumo_emulation := config.get(CONF_ENABLE_KUMO_EMULATION): - cg.add(getattr(muart_component, "set_kumo_emulation_mode")(kumo_emulation)) + if enhanced_mhk_protocol := config.get(CONF_ENHANCED_MHK_SUPPORT): + cg.add(getattr(muart_component, "set_enhanced_mhk_support")(enhanced_mhk_protocol)) diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index bf4acf08e17c..3124ea798030 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -36,13 +36,13 @@ void MitsubishiUART::process_packet(const ConnectResponsePacket &packet) { ESP_LOGI(TAG, "Heatpump connected."); }; -void MitsubishiUART::process_packet(const BaseCapabilitiesRequestPacket &packet) { +void MitsubishiUART::process_packet(const CapabilitiesRequestPacket &packet) { // Nothing to be done for these except forward them along from thermostat to heat pump. // This method defined so that these packets are not "unhandled" ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); route_packet_(packet); }; -void MitsubishiUART::process_packet(const BaseCapabilitiesResponsePacket &packet) { +void MitsubishiUART::process_packet(const CapabilitiesResponsePacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); route_packet_(packet); // Not sure if there's any needed content in this response, so assume we're connected. @@ -56,11 +56,11 @@ void MitsubishiUART::process_packet(const GetRequestPacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); switch (packet.get_requested_command()) { - case GetCommand::KUMO_GET_ADAPTER_STATE: - this->handle_kumo_adapter_state_get_request(packet); + case GetCommand::THERMOSTAT_STATE_DOWNLOAD: + this->handle_thermostat_state_download_request(packet); break; - case GetCommand::KUMO_AB: - this->handle_kumo_aa_get_request(packet); + case GetCommand::THERMOSTAT_GET_AB: + this->handle_thermostat_ab_get_request(packet); break; default: route_packet_(packet); @@ -383,8 +383,11 @@ void MitsubishiUART::process_packet(const RemoteTemperatureSetRequestPacket &pac } } -void MitsubishiUART::process_packet(const KumoThermostatSensorStatusPacket &packet) { - if (!kumo_emulation_mode_) return; +void MitsubishiUART::process_packet(const ThermostatSensorStatusPacket &packet) { + if (!enhanced_mhk_support_) { + route_packet_(packet); + return; + }; ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); @@ -403,16 +406,22 @@ void MitsubishiUART::process_packet(const KumoThermostatSensorStatusPacket &pack ts_bridge_->send_packet(SetResponsePacket()); } -void MitsubishiUART::process_packet(const KumoThermostatHelloPacket &packet) { - if (!kumo_emulation_mode_) return; +void MitsubishiUART::process_packet(const ThermostatHelloPacket &packet) { + if (!enhanced_mhk_support_) { + route_packet_(packet); + return; + }; ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); ts_bridge_->send_packet(SetResponsePacket()); } -void MitsubishiUART::process_packet(const KumoThermostatStateSyncPacket &packet) { - if (!kumo_emulation_mode_) return; +void MitsubishiUART::process_packet(const ThermostatStateUploadPacket &packet) { + if (!enhanced_mhk_support_) { + route_packet_(packet); + return; + }; ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); @@ -422,10 +431,13 @@ void MitsubishiUART::process_packet(const KumoThermostatStateSyncPacket &packet) ts_bridge_->send_packet(SetResponsePacket()); } -void MitsubishiUART::process_packet(const KumoAASetRequestPacket &packet) { - if (!kumo_emulation_mode_) return; +void MitsubishiUART::process_packet(const ThermostatAASetRequestPacket &packet) { + if (!enhanced_mhk_support_) { + route_packet_(packet); + return; + }; - ESP_LOGV(TAG, "Processing inbound KumoAASetRequestPacket: %s", packet.to_string().c_str()); + ESP_LOGV(TAG, "Processing inbound ThermostatAASetRequestPacket: %s", packet.to_string().c_str()); ts_bridge_->send_packet(SetResponsePacket()); } @@ -436,29 +448,35 @@ void MitsubishiUART::process_packet(const SetResponsePacket &packet) { route_packet_(packet); } -// Process incoming data requests (Kumo) -void MitsubishiUART::handle_kumo_adapter_state_get_request(const GetRequestPacket &packet) { - if (!kumo_emulation_mode_) return; +// Process incoming data requests from an MHK probing for/running in enhanced mode +void MitsubishiUART::handle_thermostat_state_download_request(const GetRequestPacket &packet) { + if (!enhanced_mhk_support_) { + route_packet_(packet); + return; + }; - auto response = KumoCloudStateSyncPacket(); + auto response = ThermostatStateDownloadResponsePacket(); response.set_heat_setpoint(this->last_heat_setpoint_); response.set_cool_setpoint(this->last_cool_setpoint_); - if (this->time_source != nullptr) { - response.set_timestamp(this->time_source->now()); + if (this->time_source_ != nullptr) { + response.set_timestamp(this->time_source_->now()); } else { ESP_LOGW(TAG, "No time source specified. Cannot provide accurate time!"); - response.set_timestamp(ESPTime::from_epoch_utc(1704067200)); // 2024-01-01 00:00:00 Z + response.set_timestamp(ESPTime::from_epoch_utc(1704067200)); // 2024-01-01 00:00:00Z } ts_bridge_->send_packet(response); } -void MitsubishiUART::handle_kumo_aa_get_request(const GetRequestPacket &packet) { - if (!kumo_emulation_mode_) return; +void MitsubishiUART::handle_thermostat_ab_get_request(const GetRequestPacket &packet) { + if (!enhanced_mhk_support_) { + route_packet_(packet); + return; + }; - auto response = KumoABGetRequestPacket(); + auto response = ThermostatABGetResponsePacket(); ts_bridge_->send_packet(response); } diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp index 6db1d8549710..0b88074335fe 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp @@ -115,8 +115,8 @@ void MitsubishiUART::dump_config() { ESP_LOGCONFIG(TAG, "Discovered Capabilities: %s", capabilities_cache_.value().to_string().c_str()); } - if (kumo_emulation_mode_) { - ESP_LOGCONFIG(TAG, "Kumo Emulation Mode is ENABLED! This is an *experimental mode* and things may break."); + if (enhanced_mhk_support_) { + ESP_LOGCONFIG(TAG, "MHK Enhanced Protocol Mode is ENABLED! This is currently *experimental* and things may break!"); } } @@ -150,7 +150,7 @@ void MitsubishiUART::update() { // autoconf. // For now, just requesting it as part of our "init loops" is a good first step. if (!this->capabilities_requested_) { - IFACTIVE(hp_bridge_.send_packet(BaseCapabilitiesRequestPacket::instance()); this->capabilities_requested_ = true;) + IFACTIVE(hp_bridge_.send_packet(CapabilitiesRequestPacket::instance()); this->capabilities_requested_ = true;) } // Before requesting additional updates, publish any changes waiting from packets received diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.h b/esphome/components/mitsubishi_itp/mitsubishi_uart.h index 39ee2a92d339..f1c35df8a2bb 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.h +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.h @@ -104,9 +104,9 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void set_active_mode(const bool active) { active_mode_ = active; }; // Turns on or off Kumo emulation mode - void set_kumo_emulation_mode(const bool mode) { kumo_emulation_mode_ = mode; } + void set_enhanced_mhk_support(const bool mode) { enhanced_mhk_support_ = mode; } - void set_time_source(time::RealTimeClock *rtc) { time_source = rtc; } + void set_time_source(time::RealTimeClock *rtc) { time_source_ = rtc; } protected: void route_packet_(const Packet &packet); @@ -114,8 +114,8 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void process_packet(const Packet &packet) override; void process_packet(const ConnectRequestPacket &packet) override; void process_packet(const ConnectResponsePacket &packet) override; - void process_packet(const BaseCapabilitiesRequestPacket &packet) override; - void process_packet(const BaseCapabilitiesResponsePacket &packet) override; + void process_packet(const CapabilitiesRequestPacket &packet) override; + void process_packet(const CapabilitiesResponsePacket &packet) override; void process_packet(const GetRequestPacket &packet) override; void process_packet(const SettingsGetResponsePacket &packet) override; void process_packet(const CurrentTempGetResponsePacket &packet) override; @@ -124,14 +124,14 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void process_packet(const ErrorStateGetResponsePacket &packet) override; void process_packet(const SettingsSetRequestPacket &packet) override; void process_packet(const RemoteTemperatureSetRequestPacket &packet) override; - void process_packet(const KumoThermostatSensorStatusPacket &packet) override; - void process_packet(const KumoThermostatHelloPacket &packet) override; - void process_packet(const KumoThermostatStateSyncPacket &packet) override; - void process_packet(const KumoAASetRequestPacket &packet) override; + void process_packet(const ThermostatSensorStatusPacket &packet) override; + void process_packet(const ThermostatHelloPacket &packet) override; + void process_packet(const ThermostatStateUploadPacket &packet) override; + void process_packet(const ThermostatAASetRequestPacket &packet) override; void process_packet(const SetResponsePacket &packet) override; - void handle_kumo_adapter_state_get_request(const GetRequestPacket &packet) override; - void handle_kumo_aa_get_request(const GetRequestPacket &packet) override; + void handle_thermostat_state_download_request(const GetRequestPacket &packet) override; + void handle_thermostat_ab_get_request(const GetRequestPacket &packet) override; void do_publish_(); @@ -168,7 +168,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public // Number of times update() has been called in discovery mode size_t discovery_updates_ = 0; - optional capabilities_cache_; + optional capabilities_cache_; bool capabilities_requested_ = false; // Have we received at least one RunState response? bool run_state_received_ = false; @@ -180,7 +180,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public ESPPreferenceObject preferences_; // Time Source - time::RealTimeClock *time_source = nullptr; + time::RealTimeClock *time_source_ = nullptr; // Internal sensors sensor::Sensor *thermostat_temperature_sensor_ = nullptr; @@ -209,7 +209,8 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void send_if_active_(const Packet &packet); bool active_mode_ = true; - bool kumo_emulation_mode_ = false; + // used to track whether to support/handle the enhanced MHK protocol packets + bool enhanced_mhk_support_ = false; // used to track heat/cool setpoints for parity sync with MHK units. // necessary to not clobber the union of setpoints, since ESPHome doesnt gracefully handle simultaneous cool and diff --git a/esphome/components/mitsubishi_itp/muart_bridge.cpp b/esphome/components/mitsubishi_itp/muart_bridge.cpp index 5843a4fc70fb..d0c1052aac50 100644 --- a/esphome/components/mitsubishi_itp/muart_bridge.cpp +++ b/esphome/components/mitsubishi_itp/muart_bridge.cpp @@ -147,10 +147,10 @@ void MUARTBridge::classify_and_process_raw_packet_(RawPacket &pkt) const { break; case PacketType::IDENTIFY_REQUEST: - process_raw_packet_(pkt, true); + process_raw_packet_(pkt, true); break; case PacketType::IDENTIFY_RESPONSE: - process_raw_packet_(pkt, false); + process_raw_packet_(pkt, false); break; case PacketType::GET_REQUEST: @@ -173,8 +173,8 @@ void MUARTBridge::classify_and_process_raw_packet_(RawPacket &pkt) const { case GetCommand::STATUS: process_raw_packet_(pkt, false); break; - case GetCommand::KUMO_GET_ADAPTER_STATE: - process_raw_packet_(pkt, false); + case GetCommand::THERMOSTAT_STATE_DOWNLOAD: + process_raw_packet_(pkt, false); break; default: process_raw_packet_(pkt, false); @@ -188,17 +188,17 @@ void MUARTBridge::classify_and_process_raw_packet_(RawPacket &pkt) const { case SetCommand::SETTINGS: process_raw_packet_(pkt, true); break; - case SetCommand::KUMO_THERMOSTAT_SENSOR_STATUS: - process_raw_packet_(pkt, true); + case SetCommand::THERMOSTAT_SENSOR_STATUS: + process_raw_packet_(pkt, true); break; - case SetCommand::KUMO_THERMOSTAT_HELLO: - process_raw_packet_(pkt, false); + case SetCommand::THERMOSTAT_HELLO: + process_raw_packet_(pkt, false); break; - case SetCommand::KUMO_THERMOSTAT_STATE_SYNC: - process_raw_packet_(pkt, true); + case SetCommand::THERMOSTAT_STATE_UPLOAD: + process_raw_packet_(pkt, true); break; - case SetCommand::KUMO_AA: - process_raw_packet_(pkt, true); + case SetCommand::THERMOSTAT_SET_AA: + process_raw_packet_(pkt, true); break; default: process_raw_packet_(pkt, true); diff --git a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp index f7c84ad91a56..3d3e25779d2a 100644 --- a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp +++ b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp @@ -10,7 +10,7 @@ namespace mitsubishi_itp { std::string ConnectRequestPacket::to_string() const { return ("Connect Request: " + Packet::to_string()); } std::string ConnectResponsePacket::to_string() const { return ("Connect Response: " + Packet::to_string()); } -std::string BaseCapabilitiesResponsePacket::to_string() const { +std::string CapabilitiesResponsePacket::to_string() const { return ( "Identify Base Capabilities Response: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n HeatDisabled:" + (is_heat_disabled() ? "Yes" : "No") + " SupportsVane:" + (supports_vane() ? "Yes" : "No") + @@ -69,23 +69,23 @@ std::string RemoteTemperatureSetRequestPacket::to_string() const { "\n Temp:" + std::to_string(get_remote_temperature())); } -std::string KumoThermostatSensorStatusPacket::to_string() const { - return ("Kumo Thermostat Sensor Status: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + +std::string ThermostatSensorStatusPacket::to_string() const { + return ("Thermostat Sensor Status: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Indoor RH: " + std::to_string(get_indoor_humidity_percent()) + "%" + " MHK Battery: " + THERMOSTAT_BATTERY_STATE_NAMES[get_thermostat_battery_state()] + "(" + std::to_string(get_thermostat_battery_state()) + ")" + " Sensor Flags: " + std::to_string(get_sensor_flags())); } -std::string KumoThermostatHelloPacket::to_string() const { - return ("Kumo Thermostat Hello: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Model: " + get_thermostat_model() + +std::string ThermostatHelloPacket::to_string() const { + return ("Thermostat Hello: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Model: " + get_thermostat_model() + " Serial: " + get_thermostat_serial() + " Version: " + get_thermostat_version_string()); } -std::string KumoThermostatStateSyncPacket::to_string() const { +std::string ThermostatStateUploadPacket::to_string() const { uint8_t flags = get_flags(); - std::string result = "Kumo Thermostat Sync " + Packet::to_string() + CONSOLE_COLOR_PURPLE + + std::string result = "Thermostat Sync " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Flags: " + format_hex(flags) + " =>"; if (flags & TSSF_TIMESTAMP) { @@ -272,24 +272,24 @@ float CurrentTempGetResponsePacket::get_outdoor_temp() const { return enhanced_raw_temp == 0 ? NAN : MUARTUtils::temp_scale_a_to_deg_c(enhanced_raw_temp); } -// KumoThermostatHelloPacket functions -std::string KumoThermostatHelloPacket::get_thermostat_model() const { +// ThermostatHelloPacket functions +std::string ThermostatHelloPacket::get_thermostat_model() const { return MUARTUtils::decode_n_bit_string((pkt_.get_payload_bytes(1)), 4, 6); } -std::string KumoThermostatHelloPacket::get_thermostat_serial() const { +std::string ThermostatHelloPacket::get_thermostat_serial() const { return MUARTUtils::decode_n_bit_string((pkt_.get_payload_bytes(4)), 12, 6); } -std::string KumoThermostatHelloPacket::get_thermostat_version_string() const { +std::string ThermostatHelloPacket::get_thermostat_version_string() const { char buf[16]; sprintf(buf, "%02d.%02d.%02d", pkt_.get_payload_byte(13), pkt_.get_payload_byte(14), pkt_.get_payload_byte(15)); return buf; } -// KumoThermostatStateSyncPacket functions -int32_t KumoThermostatStateSyncPacket::get_thermostat_timestamp(esphome::ESPTime *outTimestamp) const { +// ThermostatStateUploadPacket functions +int32_t ThermostatStateUploadPacket::get_thermostat_timestamp(esphome::ESPTime *outTimestamp) const { int32_be_t magic; std::memcpy(&magic, pkt_.get_payload_bytes(PLINDEX_THERMOSTAT_TIMESTAMP), 4); @@ -304,36 +304,40 @@ int32_t KumoThermostatStateSyncPacket::get_thermostat_timestamp(esphome::ESPTime return outTimestamp->timestamp; } -float KumoThermostatStateSyncPacket::get_heat_setpoint() const { +float ThermostatStateUploadPacket::get_heat_setpoint() const { uint8_t enhancedRawTemp = pkt_.get_payload_byte(PLINDEX_HEAT_SETPOINT); return MUARTUtils::temp_scale_a_to_deg_c(enhancedRawTemp); } -float KumoThermostatStateSyncPacket::get_cool_setpoint() const { +float ThermostatStateUploadPacket::get_cool_setpoint() const { uint8_t enhancedRawTemp = pkt_.get_payload_byte(PLINDEX_COOL_SETPOINT); return MUARTUtils::temp_scale_a_to_deg_c(enhancedRawTemp); } -// KumoCloudStateSyncPacket functions -KumoCloudStateSyncPacket &KumoCloudStateSyncPacket::set_timestamp(esphome::ESPTime ts) { +// ThermostatStateDownloadResponsePacket functions +ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_timestamp(esphome::ESPTime ts) { int32_t encodedTimestamp = ((ts.year - 2017) << 26) | (ts.month << 22) | (ts.day_of_month << 17) | (ts.hour << 12) | (ts.minute << 6) | (ts.second); int32_t swappedTimestamp = byteswap(encodedTimestamp); - pkt_.set_payload_bytes(PLINDEX_KUMOCLOUD_TIMESTAMP, &swappedTimestamp, 4); + pkt_.set_payload_bytes(PLINDEX_ADAPTER_TIMESTAMP, &swappedTimestamp, 4); pkt_.set_payload_byte(10, 0x07); // ??? return *this; } -KumoCloudStateSyncPacket &KumoCloudStateSyncPacket::set_heat_setpoint(float highTemp) { - pkt_.set_payload_byte(PLINDEX_HEAT_SETPOINT, MUARTUtils::deg_c_to_temp_scale_a(highTemp)); +ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_heat_setpoint(float highTemp) { + uint8_t temp_a = highTemp != NAN ? MUARTUtils::deg_c_to_temp_scale_a(highTemp) : 0x00; + + pkt_.set_payload_byte(PLINDEX_HEAT_SETPOINT, temp_a); return *this; } -KumoCloudStateSyncPacket &KumoCloudStateSyncPacket::set_cool_setpoint(float lowTemp) { - pkt_.set_payload_byte(PLINDEX_COOL_SETPOINT, MUARTUtils::deg_c_to_temp_scale_a(lowTemp)); +ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_cool_setpoint(float lowTemp) { + uint8_t temp_a = lowTemp != NAN ? MUARTUtils::deg_c_to_temp_scale_a(lowTemp) : 0x00; + + pkt_.set_payload_byte(PLINDEX_COOL_SETPOINT, temp_a); return *this; } @@ -353,8 +357,8 @@ std::string ErrorStateGetResponsePacket::get_short_code() const { return {upper_alphabet[(error_code & 0xE0) >> 5], lower_alphabet[low_bits]}; } -// BaseCapabilitiesResponsePacket functions -uint8_t BaseCapabilitiesResponsePacket::get_supported_fan_speeds() const { +// CapabilitiesResponsePacket functions +uint8_t CapabilitiesResponsePacket::get_supported_fan_speeds() const { uint8_t raw_value = ((pkt_.get_payload_byte(7) & 0x10) >> 2) + ((pkt_.get_payload_byte(8) & 0x08) >> 2) + ((pkt_.get_payload_byte(9) & 0x02) >> 1); @@ -374,7 +378,7 @@ uint8_t BaseCapabilitiesResponsePacket::get_supported_fan_speeds() const { } } -climate::ClimateTraits BaseCapabilitiesResponsePacket::as_traits() const { +climate::ClimateTraits CapabilitiesResponsePacket::as_traits() const { auto ct = climate::ClimateTraits(); // always enabled diff --git a/esphome/components/mitsubishi_itp/muart_packet.h b/esphome/components/mitsubishi_itp/muart_packet.h index f1cf50ab1b2b..62c94519b312 100644 --- a/esphome/components/mitsubishi_itp/muart_packet.h +++ b/esphome/components/mitsubishi_itp/muart_packet.h @@ -93,21 +93,21 @@ class ConnectResponsePacket : public Packet { //// // Identify packets //// -class BaseCapabilitiesRequestPacket : public Packet { +class CapabilitiesRequestPacket : public Packet { public: - static BaseCapabilitiesRequestPacket &instance() { - static BaseCapabilitiesRequestPacket instance; + static CapabilitiesRequestPacket &instance() { + static CapabilitiesRequestPacket instance; return instance; } using Packet::Packet; private: - BaseCapabilitiesRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { + CapabilitiesRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { pkt_.set_payload_byte(0, 0xc9); } }; -class BaseCapabilitiesResponsePacket : public Packet { +class CapabilitiesResponsePacket : public Packet { using Packet::Packet; public: @@ -425,7 +425,7 @@ class SetRunStatePacket : public Packet { SetRunStatePacket &set_filter_reset(bool do_reset); }; -class KumoThermostatSensorStatusPacket : public Packet { +class ThermostatSensorStatusPacket : public Packet { using Packet::Packet; public: @@ -437,8 +437,8 @@ class KumoThermostatSensorStatusPacket : public Packet { THERMOSTAT_BATTERY_UNKNOWN = 0x04, }; - KumoThermostatSensorStatusPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { - pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_THERMOSTAT_SENSOR_STATUS)); + ThermostatSensorStatusPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::THERMOSTAT_SENSOR_STATUS)); } uint8_t get_indoor_humidity_percent() const { return pkt_.get_payload_byte(5); } @@ -449,12 +449,12 @@ class KumoThermostatSensorStatusPacket : public Packet { }; // Sent by MHK2 but with no response; defined to allow setResponseExpected(false) -class KumoThermostatHelloPacket : public Packet { +class ThermostatHelloPacket : public Packet { using Packet::Packet; public: - KumoThermostatHelloPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { - pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_THERMOSTAT_HELLO)); + ThermostatHelloPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::THERMOSTAT_HELLO)); } std::string get_thermostat_model() const; @@ -464,7 +464,7 @@ class KumoThermostatHelloPacket : public Packet { std::string to_string() const override; }; -class KumoThermostatStateSyncPacket : public Packet { +class ThermostatStateUploadPacket : public Packet { // Packet 0x41 - AG 0xA8 static const uint8_t PLINDEX_THERMOSTAT_TIMESTAMP = 2; @@ -480,8 +480,8 @@ class KumoThermostatStateSyncPacket : public Packet { using Packet::Packet; public: - KumoThermostatStateSyncPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { - pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_THERMOSTAT_STATE_SYNC)); + ThermostatStateUploadPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::THERMOSTAT_STATE_UPLOAD)); } int32_t get_thermostat_timestamp(ESPTime* outTimestamp) const; @@ -491,38 +491,38 @@ class KumoThermostatStateSyncPacket : public Packet { std::string to_string() const override; }; -class KumoCloudStateSyncPacket : public Packet { - static const uint8_t PLINDEX_KUMOCLOUD_TIMESTAMP = 1; +class ThermostatStateDownloadResponsePacket : public Packet { + static const uint8_t PLINDEX_ADAPTER_TIMESTAMP = 1; static const uint8_t PLINDEX_HEAT_SETPOINT = 7; static const uint8_t PLINDEX_COOL_SETPOINT = 8; using Packet::Packet; public: - KumoCloudStateSyncPacket() : Packet(RawPacket(PacketType::GET_RESPONSE, 16)) { - pkt_.set_payload_byte(0, static_cast(GetCommand::KUMO_GET_ADAPTER_STATE)); + ThermostatStateDownloadResponsePacket() : Packet(RawPacket(PacketType::GET_RESPONSE, 16)) { + pkt_.set_payload_byte(0, static_cast(GetCommand::THERMOSTAT_STATE_DOWNLOAD)); } - KumoCloudStateSyncPacket &set_timestamp(ESPTime ts); - KumoCloudStateSyncPacket &set_heat_setpoint(float highTemp); - KumoCloudStateSyncPacket &set_cool_setpoint(float lowTemp); + ThermostatStateDownloadResponsePacket &set_timestamp(ESPTime ts); + ThermostatStateDownloadResponsePacket &set_heat_setpoint(float highTemp); + ThermostatStateDownloadResponsePacket &set_cool_setpoint(float lowTemp); }; -class KumoAASetRequestPacket : public Packet { +class ThermostatAASetRequestPacket : public Packet { using Packet::Packet; public: - KumoAASetRequestPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { - pkt_.set_payload_byte(0, static_cast(SetCommand::KUMO_AA)); + ThermostatAASetRequestPacket() : Packet(RawPacket(PacketType::SET_REQUEST, 16)) { + pkt_.set_payload_byte(0, static_cast(SetCommand::THERMOSTAT_SET_AA)); } }; -class KumoABGetRequestPacket : public Packet { +class ThermostatABGetResponsePacket : public Packet { using Packet::Packet; public: - KumoABGetRequestPacket() : Packet(RawPacket(PacketType::GET_RESPONSE, 16)) { - pkt_.set_payload_byte(0, static_cast(GetCommand::KUMO_AB)); + ThermostatABGetResponsePacket() : Packet(RawPacket(PacketType::GET_RESPONSE, 16)) { + pkt_.set_payload_byte(0, static_cast(GetCommand::THERMOSTAT_GET_AB)); pkt_.set_payload_byte(1, 1); } }; @@ -532,8 +532,8 @@ class PacketProcessor { virtual void process_packet(const Packet &packet){}; virtual void process_packet(const ConnectRequestPacket &packet){}; virtual void process_packet(const ConnectResponsePacket &packet){}; - virtual void process_packet(const BaseCapabilitiesRequestPacket &packet){}; - virtual void process_packet(const BaseCapabilitiesResponsePacket &packet){}; + virtual void process_packet(const CapabilitiesRequestPacket &packet){}; + virtual void process_packet(const CapabilitiesResponsePacket &packet){}; virtual void process_packet(const GetRequestPacket &packet){}; virtual void process_packet(const SettingsGetResponsePacket &packet){}; virtual void process_packet(const CurrentTempGetResponsePacket &packet){}; @@ -542,16 +542,16 @@ class PacketProcessor { virtual void process_packet(const ErrorStateGetResponsePacket &packet){}; virtual void process_packet(const SettingsSetRequestPacket &packet){}; virtual void process_packet(const RemoteTemperatureSetRequestPacket &packet){}; - virtual void process_packet(const KumoThermostatSensorStatusPacket &packet){}; - virtual void process_packet(const KumoThermostatHelloPacket &packet){}; - virtual void process_packet(const KumoThermostatStateSyncPacket &packet){}; - virtual void process_packet(const KumoCloudStateSyncPacket &packet){}; - virtual void process_packet(const KumoAASetRequestPacket &packet){}; - virtual void process_packet(const KumoABGetRequestPacket &packet){}; + virtual void process_packet(const ThermostatSensorStatusPacket &packet){}; + virtual void process_packet(const ThermostatHelloPacket &packet){}; + virtual void process_packet(const ThermostatStateUploadPacket &packet){}; + virtual void process_packet(const ThermostatStateDownloadResponsePacket &packet){}; + virtual void process_packet(const ThermostatAASetRequestPacket &packet){}; + virtual void process_packet(const ThermostatABGetResponsePacket &packet){}; virtual void process_packet(const SetResponsePacket &packet){}; - virtual void handle_kumo_adapter_state_get_request(const GetRequestPacket &packet){}; - virtual void handle_kumo_aa_get_request(const GetRequestPacket &packet){}; + virtual void handle_thermostat_state_download_request(const GetRequestPacket &packet){}; + virtual void handle_thermostat_ab_get_request(const GetRequestPacket &packet){}; }; } // namespace mitsubishi_itp diff --git a/esphome/components/mitsubishi_itp/muart_rawpacket.h b/esphome/components/mitsubishi_itp/muart_rawpacket.h index 23cd9e32ded0..62e224919290 100644 --- a/esphome/components/mitsubishi_itp/muart_rawpacket.h +++ b/esphome/components/mitsubishi_itp/muart_rawpacket.h @@ -35,8 +35,8 @@ enum class GetCommand : uint8_t { ERROR_INFO = 0x04, STATUS = 0x06, RUN_STATE = 0x09, - KUMO_GET_ADAPTER_STATE = 0xa9, - KUMO_AB = 0xab, + THERMOSTAT_STATE_DOWNLOAD = 0xa9, + THERMOSTAT_GET_AB = 0xab, }; // Used to specify certain packet subtypes @@ -44,10 +44,10 @@ enum class SetCommand : uint8_t { SETTINGS = 0x01, REMOTE_TEMPERATURE = 0x07, RUN_STATE = 0x08, - KUMO_THERMOSTAT_SENSOR_STATUS = 0xa6, - KUMO_THERMOSTAT_HELLO = 0xa7, - KUMO_THERMOSTAT_STATE_SYNC = 0xa8, - KUMO_AA = 0xaa, + THERMOSTAT_SENSOR_STATUS = 0xa6, + THERMOSTAT_HELLO = 0xa7, + THERMOSTAT_STATE_UPLOAD = 0xa8, + THERMOSTAT_SET_AA = 0xaa, }; // Which MUARTBridge was the packet read from (used to determine flow direction of the packet) From 3e7f81bfe080993fcd7470b42f338651add3bfb1 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 19 Jun 2024 22:37:19 -0700 Subject: [PATCH 07/15] Move MHK sync state to its own struct --- .../mitsubishi_uart-climatecall.cpp | 13 +++++++------ .../mitsubishi_uart-packetprocessing.cpp | 15 +++++++++------ .../components/mitsubishi_itp/mitsubishi_uart.h | 7 ++----- esphome/components/mitsubishi_itp/muart_mhk.h | 11 +++++++++++ 4 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 esphome/components/mitsubishi_itp/muart_mhk.h diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp index 6aac0196eec8..c954d24006b2 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp @@ -83,18 +83,19 @@ void MitsubishiUART::control(const climate::ClimateCall &call) { switch (mode) { case climate::CLIMATE_MODE_COOL: case climate::CLIMATE_MODE_DRY: - this->last_cool_setpoint_ = target_temperature; + this->mhk_state_.cool_setpoint_ = target_temperature; break; case climate::CLIMATE_MODE_HEAT: - this->last_heat_setpoint_ = target_temperature; + this->mhk_state_.heat_setpoint_ = target_temperature; break; case climate::CLIMATE_MODE_HEAT_COOL: if (this->get_traits().get_supports_two_point_target_temperature()) { - this->last_heat_setpoint_ = target_temperature_low; - this->last_cool_setpoint_ = target_temperature_high; + this->mhk_state_.cool_setpoint_ = target_temperature_low; + this->mhk_state_.heat_setpoint_ = target_temperature_high; } else { - // this->last_heat_setpoint_ = target_temperature; - // this->last_cool_setpoint_ = target_temperature; + // HACK: This is not accurate, but it's good enough for testing. + this->mhk_state_.cool_setpoint_ = target_temperature + 2; + this->mhk_state_.heat_setpoint_ = target_temperature - 2; } default: break; diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index 3124ea798030..d1eac042bd7b 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -121,11 +121,14 @@ void MitsubishiUART::process_packet(const SettingsGetResponsePacket &packet) { switch (mode) { case climate::CLIMATE_MODE_COOL: case climate::CLIMATE_MODE_DRY: - this->last_cool_setpoint_ = target_temperature; + this->mhk_state_.cool_setpoint_ = target_temperature; break; case climate::CLIMATE_MODE_HEAT: - this->last_heat_setpoint_ = target_temperature; + this->mhk_state_.heat_setpoint_ = target_temperature; break; + case climate::CLIMATE_MODE_HEAT_COOL: + this->mhk_state_.cool_setpoint_ = target_temperature + 2; + this->mhk_state_.heat_setpoint_ = target_temperature - 2; default: break; } @@ -425,8 +428,8 @@ void MitsubishiUART::process_packet(const ThermostatStateUploadPacket &packet) { ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); - if (packet.get_flags() & 0x08) this->last_heat_setpoint_ = packet.get_heat_setpoint(); - if (packet.get_flags() & 0x10) this->last_cool_setpoint_ = packet.get_cool_setpoint(); + if (packet.get_flags() & 0x08) this->mhk_state_.heat_setpoint_ = packet.get_heat_setpoint(); + if (packet.get_flags() & 0x10) this->mhk_state_.cool_setpoint_ = packet.get_cool_setpoint(); ts_bridge_->send_packet(SetResponsePacket()); } @@ -457,8 +460,8 @@ void MitsubishiUART::handle_thermostat_state_download_request(const GetRequestPa auto response = ThermostatStateDownloadResponsePacket(); - response.set_heat_setpoint(this->last_heat_setpoint_); - response.set_cool_setpoint(this->last_cool_setpoint_); + response.set_heat_setpoint(this->mhk_state_.heat_setpoint_); + response.set_cool_setpoint(this->mhk_state_.cool_setpoint_); if (this->time_source_ != nullptr) { response.set_timestamp(this->time_source_->now()); diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.h b/esphome/components/mitsubishi_itp/mitsubishi_uart.h index f1c35df8a2bb..e9db59ee0db9 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.h +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.h @@ -8,6 +8,7 @@ #include "esphome/components/climate/climate.h" #include "esphome/components/select/select.h" #include "esphome/components/sensor/sensor.h" +#include "muart_mhk.h" #include "muart_packet.h" #include "muart_bridge.h" #include @@ -212,11 +213,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public // used to track whether to support/handle the enhanced MHK protocol packets bool enhanced_mhk_support_ = false; - // used to track heat/cool setpoints for parity sync with MHK units. - // necessary to not clobber the union of setpoints, since ESPHome doesnt gracefully handle simultaneous cool and - // heat setpoints being set. - float last_cool_setpoint_ = NAN; - float last_heat_setpoint_ = NAN; + MHKState mhk_state_; }; struct MUARTPreferences { diff --git a/esphome/components/mitsubishi_itp/muart_mhk.h b/esphome/components/mitsubishi_itp/muart_mhk.h new file mode 100644 index 000000000000..b1851f065e8e --- /dev/null +++ b/esphome/components/mitsubishi_itp/muart_mhk.h @@ -0,0 +1,11 @@ +namespace esphome { +namespace mitsubishi_itp { + +/// A struct that represents the connected MHK's state for management and synchronization purposes. +struct MHKState { + float cool_setpoint_ = NAN; + float heat_setpoint_ = NAN; +}; + +} // namespace mitsubishi_itp +} // namespace esphome From fd765ce281d68ff5baa71dab30cc2ef524472101 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 19 Jun 2024 22:21:26 -0700 Subject: [PATCH 08/15] Add auto mode field to thermostat state packets --- .../mitsubishi_uart-packetprocessing.cpp | 1 + esphome/components/mitsubishi_itp/mitsubishi_uart.h | 2 +- esphome/components/mitsubishi_itp/muart_mhk.h | 7 +++++++ .../components/mitsubishi_itp/muart_packet-derived.cpp | 10 ++++++++++ esphome/components/mitsubishi_itp/muart_packet.h | 5 +++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index d1eac042bd7b..3926ad168bdf 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -460,6 +460,7 @@ void MitsubishiUART::handle_thermostat_state_download_request(const GetRequestPa auto response = ThermostatStateDownloadResponsePacket(); + response.set_auto_mode((mode == climate::CLIMATE_MODE_HEAT_COOL || mode == climate::CLIMATE_MODE_AUTO)); response.set_heat_setpoint(this->mhk_state_.heat_setpoint_); response.set_cool_setpoint(this->mhk_state_.cool_setpoint_); diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.h b/esphome/components/mitsubishi_itp/mitsubishi_uart.h index e9db59ee0db9..9f4e89fce925 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.h +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.h @@ -8,9 +8,9 @@ #include "esphome/components/climate/climate.h" #include "esphome/components/select/select.h" #include "esphome/components/sensor/sensor.h" -#include "muart_mhk.h" #include "muart_packet.h" #include "muart_bridge.h" +#include "muart_mhk.h" #include namespace esphome { diff --git a/esphome/components/mitsubishi_itp/muart_mhk.h b/esphome/components/mitsubishi_itp/muart_mhk.h index b1851f065e8e..e1f2fae99acb 100644 --- a/esphome/components/mitsubishi_itp/muart_mhk.h +++ b/esphome/components/mitsubishi_itp/muart_mhk.h @@ -1,3 +1,8 @@ +#ifndef MITSUBISHIITP_MHK +#define MITSUBISHIITP_MHK + +#include + namespace esphome { namespace mitsubishi_itp { @@ -9,3 +14,5 @@ struct MHKState { } // namespace mitsubishi_itp } // namespace esphome + +#endif // MITSUBISHIITP_MHK \ No newline at end of file diff --git a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp index 3d3e25779d2a..275b1e547d75 100644 --- a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp +++ b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp @@ -95,6 +95,7 @@ std::string ThermostatStateUploadPacket::to_string() const { result += " TS Time: " + timestamp.strftime("%Y-%m-%d %H:%M:%S"); } + if (flags & TSSF_AUTO_MODE) result += " AutoMode: " + std::to_string(get_auto_mode()); if (flags & TSSF_HEAT_SETPOINT) result += " HeatSetpoint: " + std::to_string(get_heat_setpoint()); if (flags & TSSF_COOL_SETPOINT) result += " CoolSetpoint: " + std::to_string(get_cool_setpoint()); @@ -304,6 +305,10 @@ int32_t ThermostatStateUploadPacket::get_thermostat_timestamp(esphome::ESPTime * return outTimestamp->timestamp; } +uint8_t ThermostatStateUploadPacket::get_auto_mode() const { + return pkt_.get_payload_byte(PLINDEX_AUTO_MODE); +} + float ThermostatStateUploadPacket::get_heat_setpoint() const { uint8_t enhancedRawTemp = pkt_.get_payload_byte(PLINDEX_HEAT_SETPOINT); return MUARTUtils::temp_scale_a_to_deg_c(enhancedRawTemp); @@ -327,6 +332,11 @@ ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::se return *this; } +ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_auto_mode(bool is_auto) { + pkt_.set_payload_byte(PLINDEX_AUTO_MODE, is_auto ? 0x01 : 0x00); + return *this; +} + ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_heat_setpoint(float highTemp) { uint8_t temp_a = highTemp != NAN ? MUARTUtils::deg_c_to_temp_scale_a(highTemp) : 0x00; diff --git a/esphome/components/mitsubishi_itp/muart_packet.h b/esphome/components/mitsubishi_itp/muart_packet.h index 62c94519b312..8dda6dd4d6a5 100644 --- a/esphome/components/mitsubishi_itp/muart_packet.h +++ b/esphome/components/mitsubishi_itp/muart_packet.h @@ -468,11 +468,13 @@ class ThermostatStateUploadPacket : public Packet { // Packet 0x41 - AG 0xA8 static const uint8_t PLINDEX_THERMOSTAT_TIMESTAMP = 2; + static const uint8_t PLINDEX_AUTO_MODE = 7; static const uint8_t PLINDEX_HEAT_SETPOINT = 8; static const uint8_t PLINDEX_COOL_SETPOINT = 9; enum TSStateSyncFlags : uint8_t { TSSF_TIMESTAMP = 0x01, + TSSF_AUTO_MODE = 0x04, TSSF_HEAT_SETPOINT = 0x08, TSSF_COOL_SETPOINT = 0x10, }; @@ -485,6 +487,7 @@ class ThermostatStateUploadPacket : public Packet { } int32_t get_thermostat_timestamp(ESPTime* outTimestamp) const; + uint8_t get_auto_mode() const; float get_heat_setpoint() const; float get_cool_setpoint() const; @@ -493,6 +496,7 @@ class ThermostatStateUploadPacket : public Packet { class ThermostatStateDownloadResponsePacket : public Packet { static const uint8_t PLINDEX_ADAPTER_TIMESTAMP = 1; + static const uint8_t PLINDEX_AUTO_MODE = 6; static const uint8_t PLINDEX_HEAT_SETPOINT = 7; static const uint8_t PLINDEX_COOL_SETPOINT = 8; @@ -504,6 +508,7 @@ class ThermostatStateDownloadResponsePacket : public Packet { } ThermostatStateDownloadResponsePacket &set_timestamp(ESPTime ts); + ThermostatStateDownloadResponsePacket &set_auto_mode(bool is_auto); ThermostatStateDownloadResponsePacket &set_heat_setpoint(float highTemp); ThermostatStateDownloadResponsePacket &set_cool_setpoint(float lowTemp); }; From 3e4a05ae8653299037f3d76fe7aa6a53bdc5ba5b Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 20 Jun 2024 21:18:05 -0700 Subject: [PATCH 09/15] Add log statements --- .../mitsubishi_uart-packetprocessing.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index 3926ad168bdf..adcab51c99cd 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -388,9 +388,11 @@ void MitsubishiUART::process_packet(const RemoteTemperatureSetRequestPacket &pac void MitsubishiUART::process_packet(const ThermostatSensorStatusPacket &packet) { if (!enhanced_mhk_support_) { + ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); + route_packet_(packet); return; - }; + } ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); @@ -411,20 +413,23 @@ void MitsubishiUART::process_packet(const ThermostatSensorStatusPacket &packet) void MitsubishiUART::process_packet(const ThermostatHelloPacket &packet) { if (!enhanced_mhk_support_) { + ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); + route_packet_(packet); return; - }; + } ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); - ts_bridge_->send_packet(SetResponsePacket()); } void MitsubishiUART::process_packet(const ThermostatStateUploadPacket &packet) { if (!enhanced_mhk_support_) { + ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); + route_packet_(packet); return; - }; + } ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); @@ -436,9 +441,11 @@ void MitsubishiUART::process_packet(const ThermostatStateUploadPacket &packet) { void MitsubishiUART::process_packet(const ThermostatAASetRequestPacket &packet) { if (!enhanced_mhk_support_) { + ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); + route_packet_(packet); return; - }; + } ESP_LOGV(TAG, "Processing inbound ThermostatAASetRequestPacket: %s", packet.to_string().c_str()); From 2dea0dce2af5358180a66b22a5957a3ae4a42f9a Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 20 Jun 2024 21:31:45 -0700 Subject: [PATCH 10/15] Update logs on other packets --- .../mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index adcab51c99cd..b351ba120122 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -25,7 +25,7 @@ void MitsubishiUART::process_packet(const Packet &packet) { void MitsubishiUART::process_packet(const ConnectRequestPacket &packet) { // Nothing to be done for these except forward them along from thermostat to heat pump. // This method defined so that these packets are not "unhandled" - ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); + ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); route_packet_(packet); }; void MitsubishiUART::process_packet(const ConnectResponsePacket &packet) { @@ -39,7 +39,7 @@ void MitsubishiUART::process_packet(const ConnectResponsePacket &packet) { void MitsubishiUART::process_packet(const CapabilitiesRequestPacket &packet) { // Nothing to be done for these except forward them along from thermostat to heat pump. // This method defined so that these packets are not "unhandled" - ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); + ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); route_packet_(packet); }; void MitsubishiUART::process_packet(const CapabilitiesResponsePacket &packet) { @@ -356,7 +356,7 @@ void MitsubishiUART::process_packet(const ErrorStateGetResponsePacket &packet) { } void MitsubishiUART::process_packet(const SettingsSetRequestPacket &packet) { - ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); + ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); // forward this packet as-is; we're just intercepting to log. route_packet_(packet); @@ -447,7 +447,7 @@ void MitsubishiUART::process_packet(const ThermostatAASetRequestPacket &packet) return; } - ESP_LOGV(TAG, "Processing inbound ThermostatAASetRequestPacket: %s", packet.to_string().c_str()); + ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); ts_bridge_->send_packet(SetResponsePacket()); } From c9af76697b12908b2014d81ff4d0ce8f4995db76 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 20 Jun 2024 21:58:13 -0700 Subject: [PATCH 11/15] lint pass --- esphome/components/mitsubishi_itp/climate.py | 26 ++++++++++++++----- esphome/components/mitsubishi_itp/muart_mhk.h | 5 +--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/esphome/components/mitsubishi_itp/climate.py b/esphome/components/mitsubishi_itp/climate.py index 36796b7b2223..793d094c24f9 100644 --- a/esphome/components/mitsubishi_itp/climate.py +++ b/esphome/components/mitsubishi_itp/climate.py @@ -74,7 +74,9 @@ ) CONF_DISABLE_ACTIVE_MODE = "disable_active_mode" -CONF_ENHANCED_MHK_SUPPORT = "enhanced_mhk" # EXPERIMENTAL. Will be set to default eventually. +CONF_ENHANCED_MHK_SUPPORT = ( + "enhanced_mhk" # EXPERIMENTAL. Will be set to default eventually. +) DEFAULT_POLLING_INTERVAL = "5s" @@ -172,14 +174,14 @@ state_class=STATE_CLASS_MEASUREMENT, accuracy_decimals=0, ), - sensor.register_sensor + sensor.register_sensor, ), CONF_THERMOSTAT_BATTERY: ( "Thermostat Battery", text_sensor.text_sensor_schema( icon="mdi:battery", ), - text_sensor.register_text_sensor + text_sensor.register_text_sensor, ), "compressor_frequency": ( "Compressor Frequency", @@ -340,7 +342,9 @@ async def to_code(config): rtc_component = await cg.get_variable(config[CONF_TIME_SOURCE]) cg.add(getattr(muart_component, "set_time_source")(rtc_component)) elif CONF_UART_THERMOSTAT in config and config.get(CONF_ENHANCED_MHK_SUPPORT): - raise cv.RequiredFieldInvalid(f"{CONF_TIME_SOURCE} is required if {CONF_ENHANCED_MHK_SUPPORT} is set.") + raise cv.RequiredFieldInvalid( + f"{CONF_TIME_SOURCE} is required if {CONF_ENHANCED_MHK_SUPPORT} is set." + ) # Traits traits = muart_component.config_traits() @@ -362,8 +366,14 @@ async def to_code(config): registration_function, ) in SENSORS.items(): # Only add the thermostat temp if we have a TS_UART - if ((CONF_UART_THERMOSTAT not in config) and - (sensor_designator in [CONF_THERMOSTAT_TEMPERATURE, CONF_THERMOSTAT_HUMIDITY, CONF_THERMOSTAT_BATTERY])): + if (CONF_UART_THERMOSTAT not in config) and ( + sensor_designator + in [ + CONF_THERMOSTAT_TEMPERATURE, + CONF_THERMOSTAT_HUMIDITY, + CONF_THERMOSTAT_BATTERY, + ] + ): continue sensor_conf = config[CONF_SENSORS][sensor_designator] @@ -424,4 +434,6 @@ async def to_code(config): cg.add(getattr(muart_component, "set_active_mode")(not dam_conf)) if enhanced_mhk_protocol := config.get(CONF_ENHANCED_MHK_SUPPORT): - cg.add(getattr(muart_component, "set_enhanced_mhk_support")(enhanced_mhk_protocol)) + cg.add( + getattr(muart_component, "set_enhanced_mhk_support")(enhanced_mhk_protocol) + ) diff --git a/esphome/components/mitsubishi_itp/muart_mhk.h b/esphome/components/mitsubishi_itp/muart_mhk.h index e1f2fae99acb..4cd75f4b0825 100644 --- a/esphome/components/mitsubishi_itp/muart_mhk.h +++ b/esphome/components/mitsubishi_itp/muart_mhk.h @@ -1,5 +1,4 @@ -#ifndef MITSUBISHIITP_MHK -#define MITSUBISHIITP_MHK +#pragma once #include @@ -14,5 +13,3 @@ struct MHKState { } // namespace mitsubishi_itp } // namespace esphome - -#endif // MITSUBISHIITP_MHK \ No newline at end of file From 0ca0e23386b324adf8c85ce46003ad3685ee8676 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sat, 22 Jun 2024 14:25:21 -0700 Subject: [PATCH 12/15] lint pass 2 --- .../mitsubishi_itp/muart_packet-derived.cpp | 64 ++++++++++--------- .../components/mitsubishi_itp/muart_packet.h | 22 +++---- .../mitsubishi_itp/muart_rawpacket.cpp | 4 +- .../mitsubishi_itp/muart_rawpacket.h | 4 +- tests/components/mitsubishi_itp/common.yaml | 12 ++++ 5 files changed, 62 insertions(+), 44 deletions(-) diff --git a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp index 275b1e547d75..94ae73378ad3 100644 --- a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp +++ b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp @@ -29,9 +29,7 @@ std::string CapabilitiesResponsePacket::to_string() const { "/" + std::to_string(get_max_heating_setpoint()) + " AutoSetpoint:" + std::to_string(get_min_auto_setpoint()) + "/" + std::to_string(get_max_auto_setpoint()) + " FanSpeeds:" + std::to_string(get_supported_fan_speeds())); } -std::string IdentifyCDResponsePacket::to_string() const { - return "Identify CD Response: " + Packet::to_string(); -} +std::string IdentifyCDResponsePacket::to_string() const { return "Identify CD Response: " + Packet::to_string(); } std::string CurrentTempGetResponsePacket::to_string() const { return ("Current Temp Response: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Temp:" + std::to_string(get_current_temp()) + @@ -72,8 +70,8 @@ std::string RemoteTemperatureSetRequestPacket::to_string() const { std::string ThermostatSensorStatusPacket::to_string() const { return ("Thermostat Sensor Status: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Indoor RH: " + std::to_string(get_indoor_humidity_percent()) + "%" + - " MHK Battery: " + THERMOSTAT_BATTERY_STATE_NAMES[get_thermostat_battery_state()] + - "(" + std::to_string(get_thermostat_battery_state()) + ")" + + " MHK Battery: " + THERMOSTAT_BATTERY_STATE_NAMES[get_thermostat_battery_state()] + "(" + + std::to_string(get_thermostat_battery_state()) + ")" + " Sensor Flags: " + std::to_string(get_sensor_flags())); } @@ -85,8 +83,8 @@ std::string ThermostatHelloPacket::to_string() const { std::string ThermostatStateUploadPacket::to_string() const { uint8_t flags = get_flags(); - std::string result = "Thermostat Sync " + Packet::to_string() + CONSOLE_COLOR_PURPLE + - "\n Flags: " + format_hex(flags) + " =>"; + std::string result = + "Thermostat Sync " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Flags: " + format_hex(flags) + " =>"; if (flags & TSSF_TIMESTAMP) { ESPTime timestamp{}; @@ -95,9 +93,12 @@ std::string ThermostatStateUploadPacket::to_string() const { result += " TS Time: " + timestamp.strftime("%Y-%m-%d %H:%M:%S"); } - if (flags & TSSF_AUTO_MODE) result += " AutoMode: " + std::to_string(get_auto_mode()); - if (flags & TSSF_HEAT_SETPOINT) result += " HeatSetpoint: " + std::to_string(get_heat_setpoint()); - if (flags & TSSF_COOL_SETPOINT) result += " CoolSetpoint: " + std::to_string(get_cool_setpoint()); + if (flags & TSSF_AUTO_MODE) + result += " AutoMode: " + std::to_string(get_auto_mode()); + if (flags & TSSF_HEAT_SETPOINT) + result += " HeatSetpoint: " + std::to_string(get_heat_setpoint()); + if (flags & TSSF_COOL_SETPOINT) + result += " CoolSetpoint: " + std::to_string(get_cool_setpoint()); return result; } @@ -114,11 +115,16 @@ std::string SettingsSetRequestPacket::to_string() const { std::string result = "Settings Set Request: " + Packet::to_string() + CONSOLE_COLOR_PURPLE + "\n Flags: " + format_hex(flags2) + format_hex(flags) + " =>"; - if (flags & SettingFlag::SF_POWER) result += " Power: " + std::to_string(get_power()); - if (flags & SettingFlag::SF_MODE) result += " Mode: " + std::to_string(get_mode()); - if (flags & SettingFlag::SF_TARGET_TEMPERATURE) result += " TargetTemp: " + std::to_string(get_target_temp()); - if (flags & SettingFlag::SF_FAN) result += " Fan: " + std::to_string(get_fan()); - if (flags & SettingFlag::SF_VANE) result += " Vane: " + std::to_string(get_vane()); + if (flags & SettingFlag::SF_POWER) + result += " Power: " + std::to_string(get_power()); + if (flags & SettingFlag::SF_MODE) + result += " Mode: " + std::to_string(get_mode()); + if (flags & SettingFlag::SF_TARGET_TEMPERATURE) + result += " TargetTemp: " + std::to_string(get_target_temp()); + if (flags & SettingFlag::SF_FAN) + result += " Fan: " + std::to_string(get_fan()); + if (flags & SettingFlag::SF_VANE) + result += " Vane: " + std::to_string(get_vane()); if (flags2 & SettingFlag2::SF2_HORIZONTAL_VANE) result += " HVane: " + std::to_string(get_horizontal_vane()) + (get_horizontal_vane_msb() ? " (MSB Set)" : ""); @@ -305,28 +311,26 @@ int32_t ThermostatStateUploadPacket::get_thermostat_timestamp(esphome::ESPTime * return outTimestamp->timestamp; } -uint8_t ThermostatStateUploadPacket::get_auto_mode() const { - return pkt_.get_payload_byte(PLINDEX_AUTO_MODE); -} +uint8_t ThermostatStateUploadPacket::get_auto_mode() const { return pkt_.get_payload_byte(PLINDEX_AUTO_MODE); } float ThermostatStateUploadPacket::get_heat_setpoint() const { - uint8_t enhancedRawTemp = pkt_.get_payload_byte(PLINDEX_HEAT_SETPOINT); - return MUARTUtils::temp_scale_a_to_deg_c(enhancedRawTemp); + uint8_t enhanced_raw_temp = pkt_.get_payload_byte(PLINDEX_HEAT_SETPOINT); + return MUARTUtils::temp_scale_a_to_deg_c(enhanced_raw_temp); } float ThermostatStateUploadPacket::get_cool_setpoint() const { - uint8_t enhancedRawTemp = pkt_.get_payload_byte(PLINDEX_COOL_SETPOINT); - return MUARTUtils::temp_scale_a_to_deg_c(enhancedRawTemp); + uint8_t enhanced_raw_temp = pkt_.get_payload_byte(PLINDEX_COOL_SETPOINT); + return MUARTUtils::temp_scale_a_to_deg_c(enhanced_raw_temp); } // ThermostatStateDownloadResponsePacket functions ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_timestamp(esphome::ESPTime ts) { - int32_t encodedTimestamp = ((ts.year - 2017) << 26) | (ts.month << 22) | (ts.day_of_month << 17) | - (ts.hour << 12) | (ts.minute << 6) | (ts.second); + int32_t encoded_timestamp = ((ts.year - 2017) << 26) | (ts.month << 22) | (ts.day_of_month << 17) | (ts.hour << 12) | + (ts.minute << 6) | (ts.second); - int32_t swappedTimestamp = byteswap(encodedTimestamp); + int32_t swapped_timestamp = byteswap(encoded_timestamp); - pkt_.set_payload_bytes(PLINDEX_ADAPTER_TIMESTAMP, &swappedTimestamp, 4); + pkt_.set_payload_bytes(PLINDEX_ADAPTER_TIMESTAMP, &swapped_timestamp, 4); pkt_.set_payload_byte(10, 0x07); // ??? return *this; @@ -337,15 +341,15 @@ ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::se return *this; } -ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_heat_setpoint(float highTemp) { - uint8_t temp_a = highTemp != NAN ? MUARTUtils::deg_c_to_temp_scale_a(highTemp) : 0x00; +ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_heat_setpoint(float high_temp) { + uint8_t temp_a = high_temp != NAN ? MUARTUtils::deg_c_to_temp_scale_a(high_temp) : 0x00; pkt_.set_payload_byte(PLINDEX_HEAT_SETPOINT, temp_a); return *this; } -ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_cool_setpoint(float lowTemp) { - uint8_t temp_a = lowTemp != NAN ? MUARTUtils::deg_c_to_temp_scale_a(lowTemp) : 0x00; +ThermostatStateDownloadResponsePacket &ThermostatStateDownloadResponsePacket::set_cool_setpoint(float low_temp) { + uint8_t temp_a = low_temp != NAN ? MUARTUtils::deg_c_to_temp_scale_a(low_temp) : 0x00; pkt_.set_payload_byte(PLINDEX_COOL_SETPOINT, temp_a); return *this; diff --git a/esphome/components/mitsubishi_itp/muart_packet.h b/esphome/components/mitsubishi_itp/muart_packet.h index 8dda6dd4d6a5..b0446bb8056f 100644 --- a/esphome/components/mitsubishi_itp/muart_packet.h +++ b/esphome/components/mitsubishi_itp/muart_packet.h @@ -102,9 +102,7 @@ class CapabilitiesRequestPacket : public Packet { using Packet::Packet; private: - CapabilitiesRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { - pkt_.set_payload_byte(0, 0xc9); - } + CapabilitiesRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { pkt_.set_payload_byte(0, 0xc9); } }; class CapabilitiesResponsePacket : public Packet { @@ -158,9 +156,7 @@ class IdentifyCDRequestPacket : public Packet { using Packet::Packet; private: - IdentifyCDRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { - pkt_.set_payload_byte(0, 0xCD); - } + IdentifyCDRequestPacket() : Packet(RawPacket(PacketType::IDENTIFY_REQUEST, 1)) { pkt_.set_payload_byte(0, 0xCD); } }; class IdentifyCDResponsePacket : public Packet { @@ -363,7 +359,9 @@ class SettingsSetRequestPacket : public Packet { ModeByte get_mode() const { return (ModeByte) pkt_.get_payload_byte(PLINDEX_MODE); } FanByte get_fan() const { return (FanByte) pkt_.get_payload_byte(PLINDEX_FAN); } VaneByte get_vane() const { return (VaneByte) pkt_.get_payload_byte(PLINDEX_VANE); } - HorizontalVaneByte get_horizontal_vane() const { return (HorizontalVaneByte) (pkt_.get_payload_byte(PLINDEX_HORIZONTAL_VANE) & 0x7F); } + HorizontalVaneByte get_horizontal_vane() const { + return (HorizontalVaneByte) (pkt_.get_payload_byte(PLINDEX_HORIZONTAL_VANE) & 0x7F); + } bool get_horizontal_vane_msb() const { return pkt_.get_payload_byte(PLINDEX_HORIZONTAL_VANE) & 0x80; } float get_target_temp() const; @@ -442,7 +440,9 @@ class ThermostatSensorStatusPacket : public Packet { } uint8_t get_indoor_humidity_percent() const { return pkt_.get_payload_byte(5); } - ThermostatBatteryState get_thermostat_battery_state() const { return (ThermostatBatteryState) pkt_.get_payload_byte(6); } + ThermostatBatteryState get_thermostat_battery_state() const { + return (ThermostatBatteryState) pkt_.get_payload_byte(6); + } uint8_t get_sensor_flags() const { return pkt_.get_payload_byte(7); } std::string to_string() const override; @@ -486,7 +486,7 @@ class ThermostatStateUploadPacket : public Packet { pkt_.set_payload_byte(0, static_cast(SetCommand::THERMOSTAT_STATE_UPLOAD)); } - int32_t get_thermostat_timestamp(ESPTime* outTimestamp) const; + int32_t get_thermostat_timestamp(ESPTime *outTimestamp) const; uint8_t get_auto_mode() const; float get_heat_setpoint() const; float get_cool_setpoint() const; @@ -509,8 +509,8 @@ class ThermostatStateDownloadResponsePacket : public Packet { ThermostatStateDownloadResponsePacket &set_timestamp(ESPTime ts); ThermostatStateDownloadResponsePacket &set_auto_mode(bool is_auto); - ThermostatStateDownloadResponsePacket &set_heat_setpoint(float highTemp); - ThermostatStateDownloadResponsePacket &set_cool_setpoint(float lowTemp); + ThermostatStateDownloadResponsePacket &set_heat_setpoint(float high_temp); + ThermostatStateDownloadResponsePacket &set_cool_setpoint(float low_temp); }; class ThermostatAASetRequestPacket : public Packet { diff --git a/esphome/components/mitsubishi_itp/muart_rawpacket.cpp b/esphome/components/mitsubishi_itp/muart_rawpacket.cpp index dc9223cb11b2..2878945388a7 100644 --- a/esphome/components/mitsubishi_itp/muart_rawpacket.cpp +++ b/esphome/components/mitsubishi_itp/muart_rawpacket.cpp @@ -37,7 +37,7 @@ RawPacket::RawPacket() { // TODO: Is this okay? } -uint8_t RawPacket::calculate_checksum_() const { +uint8_t RawPacket::calculate_checksum_() const { // NOLINT(readability-identifier-naming) uint8_t sum = 0; for (int i = 0; i < checksum_index_; i++) { sum += packet_bytes_[i]; @@ -60,7 +60,7 @@ RawPacket &RawPacket::set_payload_byte(const uint8_t payload_byte_index, const u return *this; } -RawPacket &RawPacket::set_payload_bytes(const uint8_t begin_index, const void* value, const size_t size) { +RawPacket &RawPacket::set_payload_bytes(const uint8_t begin_index, const void *value, const size_t size) { memcpy(&packet_bytes_[PACKET_HEADER_SIZE + begin_index], value, size); update_checksum_(); return *this; diff --git a/esphome/components/mitsubishi_itp/muart_rawpacket.h b/esphome/components/mitsubishi_itp/muart_rawpacket.h index 62e224919290..c6a33129a324 100644 --- a/esphome/components/mitsubishi_itp/muart_rawpacket.h +++ b/esphome/components/mitsubishi_itp/muart_rawpacket.h @@ -100,7 +100,9 @@ class RawPacket { uint8_t get_payload_byte(const uint8_t payload_byte_index) const { return packet_bytes_[PACKET_HEADER_SIZE + payload_byte_index]; }; - const uint8_t *get_payload_bytes(size_t startIndex = 0) const { return &packet_bytes_[PACKET_HEADER_SIZE + startIndex]; } + const uint8_t *get_payload_bytes(size_t startIndex = 0) const { + return &packet_bytes_[PACKET_HEADER_SIZE + startIndex]; + } private: static const int PLINDEX_COMMAND = 0; diff --git a/tests/components/mitsubishi_itp/common.yaml b/tests/components/mitsubishi_itp/common.yaml index d0e2d68e1713..50a539f0897b 100644 --- a/tests/components/mitsubishi_itp/common.yaml +++ b/tests/components/mitsubishi_itp/common.yaml @@ -1,3 +1,14 @@ +wifi: + ssid: MySSID + password: password1 + +api: + +time: + - platform: homeassistant + id: homeassistant_time + timezone: America/Los_Angeles + sensor: - platform: template id: fake_temp @@ -25,6 +36,7 @@ climate: - platform: mitsubishi_itp uart_heatpump: hp_uart uart_thermostat: tstat_uart + time_source: homeassistant_time update_interval: 12s temperature_sources: - fake_temp From b20b3336178555ee6d23da0eb131a38690104f26 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sat, 22 Jun 2024 15:03:49 -0700 Subject: [PATCH 13/15] remove time dependency --- esphome/components/mitsubishi_itp/climate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/mitsubishi_itp/climate.py b/esphome/components/mitsubishi_itp/climate.py index 793d094c24f9..ff0143ca1d8b 100644 --- a/esphome/components/mitsubishi_itp/climate.py +++ b/esphome/components/mitsubishi_itp/climate.py @@ -42,7 +42,6 @@ ] DEPENDENCIES = [ "uart", - "time", "climate", "sensor", "binary_sensor", From 7a39c2608a1a63a4c5748d3dc2b20cc56aba5ae6 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Mon, 24 Jun 2024 19:23:02 -0700 Subject: [PATCH 14/15] Shunt around dependencies/autoload --- esphome/components/mitsubishi_itp/climate.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/esphome/components/mitsubishi_itp/climate.py b/esphome/components/mitsubishi_itp/climate.py index ff0143ca1d8b..1327e5064fab 100644 --- a/esphome/components/mitsubishi_itp/climate.py +++ b/esphome/components/mitsubishi_itp/climate.py @@ -39,15 +39,11 @@ "binary_sensor", "button", "text_sensor", + "time", ] DEPENDENCIES = [ "uart", "climate", - "sensor", - "binary_sensor", - "button", - "text_sensor", - "select", ] CONF_UART_HEATPUMP = "uart_heatpump" From c2f10768cb9559a492ef8b7d7e4ae3d9cf50e549 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Mon, 24 Jun 2024 21:37:24 -0700 Subject: [PATCH 15/15] more complaints --- .../mitsubishi_uart-climatecall.cpp | 4 +-- .../mitsubishi_uart-packetprocessing.cpp | 28 ++++++++++--------- .../mitsubishi_itp/mitsubishi_uart.cpp | 3 +- .../mitsubishi_itp/mitsubishi_uart.h | 2 +- .../mitsubishi_itp/muart_packet-derived.cpp | 18 ++++++------ .../components/mitsubishi_itp/muart_packet.h | 2 +- .../mitsubishi_itp/muart_rawpacket.h | 4 +-- 7 files changed, 31 insertions(+), 30 deletions(-) diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp index c954d24006b2..c5eebd2e1382 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-climatecall.cpp @@ -111,10 +111,10 @@ void MitsubishiUART::control(const climate::ClimateCall &call) { // Queue the packet to be sent first (so any subsequent update packets come *after* our changes) hp_bridge_.send_packet(set_request_packet); - // Publish state and any sensor changes (shouldn't be any a a result of this function, but + // Publish state and any sensor changes (shouldn't be any a result of this function, but // since they lazy-publish, no harm in trying) do_publish_(); -}; +} } // namespace mitsubishi_itp } // namespace esphome diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp index b351ba120122..f0b641887f83 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart-packetprocessing.cpp @@ -20,28 +20,28 @@ void MitsubishiUART::process_packet(const Packet &packet) { ESP_LOGI(TAG, "Generic unhandled packet type %x received.", packet.get_packet_type()); ESP_LOGD(TAG, "%s", packet.to_string().c_str()); route_packet_(packet); -}; +} void MitsubishiUART::process_packet(const ConnectRequestPacket &packet) { // Nothing to be done for these except forward them along from thermostat to heat pump. // This method defined so that these packets are not "unhandled" ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); route_packet_(packet); -}; +} void MitsubishiUART::process_packet(const ConnectResponsePacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); route_packet_(packet); // Not sure if there's any needed content in this response, so assume we're connected. hp_connected_ = true; ESP_LOGI(TAG, "Heatpump connected."); -}; +} void MitsubishiUART::process_packet(const CapabilitiesRequestPacket &packet) { // Nothing to be done for these except forward them along from thermostat to heat pump. // This method defined so that these packets are not "unhandled" ESP_LOGV(TAG, "Passing through inbound %s", packet.to_string().c_str()); route_packet_(packet); -}; +} void MitsubishiUART::process_packet(const CapabilitiesResponsePacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); route_packet_(packet); @@ -50,7 +50,7 @@ void MitsubishiUART::process_packet(const CapabilitiesResponsePacket &packet) { hp_connected_ = true; capabilities_cache_ = packet; ESP_LOGI(TAG, "Received heat pump identification packet."); -}; +} void MitsubishiUART::process_packet(const GetRequestPacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); @@ -217,7 +217,7 @@ void MitsubishiUART::process_packet(const SettingsGetResponsePacket &packet) { ESP_LOGW(TAG, "Vane in unknown horizontal position %x", packet.get_horizontal_vane()); } publish_on_update_ |= (old_horizontal_vane_position != horizontal_vane_position_select_->state); -}; +} void MitsubishiUART::process_packet(const CurrentTempGetResponsePacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); @@ -233,7 +233,7 @@ void MitsubishiUART::process_packet(const CurrentTempGetResponsePacket &packet) outdoor_temperature_sensor_->raw_state = packet.get_outdoor_temp(); publish_on_update_ |= (old_outdoor_temperature != outdoor_temperature_sensor_->raw_state); } -}; +} void MitsubishiUART::process_packet(const StatusGetResponsePacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); @@ -292,7 +292,7 @@ void MitsubishiUART::process_packet(const StatusGetResponsePacket &packet) { publish_on_update_ |= (old_compressor_frequency != compressor_frequency_sensor_->raw_state); } -}; +} void MitsubishiUART::process_packet(const RunStateGetResponsePacket &packet) { ESP_LOGV(TAG, "Processing %s", packet.to_string().c_str()); route_packet_(packet); @@ -433,8 +433,10 @@ void MitsubishiUART::process_packet(const ThermostatStateUploadPacket &packet) { ESP_LOGV(TAG, "Processing inbound %s", packet.to_string().c_str()); - if (packet.get_flags() & 0x08) this->mhk_state_.heat_setpoint_ = packet.get_heat_setpoint(); - if (packet.get_flags() & 0x10) this->mhk_state_.cool_setpoint_ = packet.get_cool_setpoint(); + if (packet.get_flags() & 0x08) + this->mhk_state_.heat_setpoint_ = packet.get_heat_setpoint(); + if (packet.get_flags() & 0x10) + this->mhk_state_.cool_setpoint_ = packet.get_cool_setpoint(); ts_bridge_->send_packet(SetResponsePacket()); } @@ -463,7 +465,7 @@ void MitsubishiUART::handle_thermostat_state_download_request(const GetRequestPa if (!enhanced_mhk_support_) { route_packet_(packet); return; - }; + } auto response = ThermostatStateDownloadResponsePacket(); @@ -475,7 +477,7 @@ void MitsubishiUART::handle_thermostat_state_download_request(const GetRequestPa response.set_timestamp(this->time_source_->now()); } else { ESP_LOGW(TAG, "No time source specified. Cannot provide accurate time!"); - response.set_timestamp(ESPTime::from_epoch_utc(1704067200)); // 2024-01-01 00:00:00Z + response.set_timestamp(ESPTime::from_epoch_utc(1704067200)); // 2024-01-01 00:00:00Z } ts_bridge_->send_packet(response); @@ -485,7 +487,7 @@ void MitsubishiUART::handle_thermostat_ab_get_request(const GetRequestPacket &pa if (!enhanced_mhk_support_) { route_packet_(packet); return; - }; + } auto response = ThermostatABGetResponsePacket(); diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp index 0b88074335fe..8738b2675d15 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.cpp @@ -207,8 +207,7 @@ void MitsubishiUART::do_publish_() { ESP_LOGI(TAG, "Outdoor temp differs, do publish"); outdoor_temperature_sensor_->publish_state(outdoor_temperature_sensor_->raw_state); } - if (thermostat_humidity_sensor_ && - (thermostat_humidity_sensor_->raw_state != thermostat_humidity_sensor_->state)) { + if (thermostat_humidity_sensor_ && (thermostat_humidity_sensor_->raw_state != thermostat_humidity_sensor_->state)) { ESP_LOGI(TAG, "Thermostat humidity differs, do publish"); thermostat_humidity_sensor_->publish_state(thermostat_humidity_sensor_->raw_state); } diff --git a/esphome/components/mitsubishi_itp/mitsubishi_uart.h b/esphome/components/mitsubishi_itp/mitsubishi_uart.h index 9f4e89fce925..a80092430483 100644 --- a/esphome/components/mitsubishi_itp/mitsubishi_uart.h +++ b/esphome/components/mitsubishi_itp/mitsubishi_uart.h @@ -105,7 +105,7 @@ class MitsubishiUART : public PollingComponent, public climate::Climate, public void set_active_mode(const bool active) { active_mode_ = active; }; // Turns on or off Kumo emulation mode - void set_enhanced_mhk_support(const bool mode) { enhanced_mhk_support_ = mode; } + void set_enhanced_mhk_support(const bool supports) { enhanced_mhk_support_ = supports; } void set_time_source(time::RealTimeClock *rtc) { time_source_ = rtc; } diff --git a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp index 94ae73378ad3..bd719b2fe3d5 100644 --- a/esphome/components/mitsubishi_itp/muart_packet-derived.cpp +++ b/esphome/components/mitsubishi_itp/muart_packet-derived.cpp @@ -296,19 +296,19 @@ std::string ThermostatHelloPacket::get_thermostat_version_string() const { } // ThermostatStateUploadPacket functions -int32_t ThermostatStateUploadPacket::get_thermostat_timestamp(esphome::ESPTime *outTimestamp) const { +time_t ThermostatStateUploadPacket::get_thermostat_timestamp(esphome::ESPTime *out_timestamp) const { int32_be_t magic; std::memcpy(&magic, pkt_.get_payload_bytes(PLINDEX_THERMOSTAT_TIMESTAMP), 4); - outTimestamp->second = magic & 63; - outTimestamp->minute = (magic >> 6) & 63; - outTimestamp->hour = (magic >> 12) & 31; - outTimestamp->day_of_month = (magic >> 17) & 31; - outTimestamp->month = (magic >> 22) & 15; - outTimestamp->year = (magic >> 26) + 2017; + out_timestamp->second = magic & 63; + out_timestamp->minute = (magic >> 6) & 63; + out_timestamp->hour = (magic >> 12) & 31; + out_timestamp->day_of_month = (magic >> 17) & 31; + out_timestamp->month = (magic >> 22) & 15; + out_timestamp->year = (magic >> 26) + 2017; - outTimestamp->recalc_timestamp_local(); - return outTimestamp->timestamp; + out_timestamp->recalc_timestamp_local(); + return out_timestamp->timestamp; } uint8_t ThermostatStateUploadPacket::get_auto_mode() const { return pkt_.get_payload_byte(PLINDEX_AUTO_MODE); } diff --git a/esphome/components/mitsubishi_itp/muart_packet.h b/esphome/components/mitsubishi_itp/muart_packet.h index b0446bb8056f..e023b71fd8e6 100644 --- a/esphome/components/mitsubishi_itp/muart_packet.h +++ b/esphome/components/mitsubishi_itp/muart_packet.h @@ -486,7 +486,7 @@ class ThermostatStateUploadPacket : public Packet { pkt_.set_payload_byte(0, static_cast(SetCommand::THERMOSTAT_STATE_UPLOAD)); } - int32_t get_thermostat_timestamp(ESPTime *outTimestamp) const; + time_t get_thermostat_timestamp(esphome::ESPTime *out_timestamp) const; uint8_t get_auto_mode() const; float get_heat_setpoint() const; float get_cool_setpoint() const; diff --git a/esphome/components/mitsubishi_itp/muart_rawpacket.h b/esphome/components/mitsubishi_itp/muart_rawpacket.h index c6a33129a324..bf317ac0d759 100644 --- a/esphome/components/mitsubishi_itp/muart_rawpacket.h +++ b/esphome/components/mitsubishi_itp/muart_rawpacket.h @@ -100,8 +100,8 @@ class RawPacket { uint8_t get_payload_byte(const uint8_t payload_byte_index) const { return packet_bytes_[PACKET_HEADER_SIZE + payload_byte_index]; }; - const uint8_t *get_payload_bytes(size_t startIndex = 0) const { - return &packet_bytes_[PACKET_HEADER_SIZE + startIndex]; + const uint8_t *get_payload_bytes(size_t start_index = 0) const { + return &packet_bytes_[PACKET_HEADER_SIZE + start_index]; } private: