diff --git a/components/panasonic_heatpump/commands.cpp b/components/panasonic_heatpump/commands.cpp index b4daff3..7d636ee 100644 --- a/components/panasonic_heatpump/commands.cpp +++ b/components/panasonic_heatpump/commands.cpp @@ -24,6 +24,16 @@ namespace esphome 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 }; + const uint8_t PanasonicCommand::PollingExtraMessage[DATA_MESSAGE_SIZE] = { + 0x71, 0x6C, 0x01, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; const uint8_t PanasonicCommand::CommandMessage[DATA_MESSAGE_SIZE] = { 0xF1, 0x6C, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/components/panasonic_heatpump/commands.h b/components/panasonic_heatpump/commands.h index 85de7a8..7a0d634 100644 --- a/components/panasonic_heatpump/commands.h +++ b/components/panasonic_heatpump/commands.h @@ -42,6 +42,7 @@ namespace esphome static const uint8_t InitialRequest[INIT_REQUEST_SIZE]; static const uint8_t InitialResponse[INIT_RESPONSE_SIZE]; static const uint8_t PollingMessage[DATA_MESSAGE_SIZE]; + static const uint8_t PollingExtraMessage[DATA_MESSAGE_SIZE]; static const uint8_t CommandMessage[DATA_MESSAGE_SIZE]; }; } // namespace panasonic_heatpump diff --git a/components/panasonic_heatpump/panasonic_heatpump.cpp b/components/panasonic_heatpump/panasonic_heatpump.cpp index 7910111..0b98c2d 100644 --- a/components/panasonic_heatpump/panasonic_heatpump.cpp +++ b/components/panasonic_heatpump/panasonic_heatpump.cpp @@ -25,8 +25,8 @@ namespace esphome void PanasonicHeatpumpComponent::update() { - if (this->uart_client_ == nullptr) - this->next_request_ = RequestType::POLLING; + if (this->uart_client_ != nullptr) return; + this->next_request_ = this->send_extra_request_ ? RequestType::POLLING_EXTRA : RequestType::POLLING; } void PanasonicHeatpumpComponent::loop() @@ -34,98 +34,91 @@ namespace esphome switch (this->loop_state_) { case LoopState::READ_RESPONSE: - { this->read_response(); this->loop_state_ = LoopState::CHECK_RESPONSE; break; - } case LoopState::CHECK_RESPONSE: - { - bool result = this->check_response(this->heatpump_message_); - this->loop_state_ = result ? - LoopState::PUBLISH_SENSOR : LoopState::SEND_REQUEST; + this->current_response_ = this->check_response(this->heatpump_message_); + switch (this->current_response_) + { + case ResponseType::UNKNOWN: + this->loop_state_ = LoopState::SEND_REQUEST; + break; + case ResponseType::DEFAULT: + this->loop_state_ = LoopState::PUBLISH_SENSOR; + break; + case ResponseType::EXTRA: + this->loop_state_ = LoopState::PUBLISH_EXTRA_SENSOR; + break; + }; break; - } case LoopState::PUBLISH_SENSOR: - { for (auto *entity : this->sensors_) { entity->publish_new_state(this->heatpump_message_); } this->loop_state_ = LoopState::PUBLISH_BINARY_SENSOR; break; - } case LoopState::PUBLISH_BINARY_SENSOR: - { for (auto *entity : this->binary_sensors_) { entity->publish_new_state(this->heatpump_message_); } this->loop_state_ = LoopState::PUBLISH_TEXT_SENSOR; break; - } case LoopState::PUBLISH_TEXT_SENSOR: - { for (auto *entity : this->text_sensors_) { entity->publish_new_state(this->heatpump_message_); } this->loop_state_ = LoopState::PUBLISH_NUMBER; break; - } case LoopState::PUBLISH_NUMBER: - { for (auto *entity : this->numbers_) { entity->publish_new_state(this->heatpump_message_); } this->loop_state_ = LoopState::PUBLISH_SELECT; break; - } case LoopState::PUBLISH_SELECT: - { for (auto *entity : this->selects_) { entity->publish_new_state(this->heatpump_message_); } this->loop_state_ = LoopState::PUBLISH_SWITCH; break; - } case LoopState::PUBLISH_SWITCH: - { for (auto *entity : this->switches_) { entity->publish_new_state(this->heatpump_message_); } this->loop_state_ = LoopState::PUBLISH_CLIMATE; break; - } case LoopState::PUBLISH_CLIMATE: - { for (auto *entity : this->climates_) { entity->publish_new_state(this->heatpump_message_); } this->loop_state_ = LoopState::SEND_REQUEST; break; - } + case LoopState::PUBLISH_EXTRA_SENSOR: + for (auto *entity : this->extra_sensors_) + { + entity->publish_new_state(this->heatpump_message_); + } + this->loop_state_ = LoopState::SEND_REQUEST; + break; case LoopState::SEND_REQUEST: - { this->send_request(this->next_request_); this->loop_state_ = LoopState::READ_REQUEST; break; - } case LoopState::READ_REQUEST: - { this->read_request(); this->loop_state_ = LoopState::RESTART_LOOP; break; - } default: - { this->loop_state_ = LoopState::READ_RESPONSE; break; - } }; } @@ -205,6 +198,13 @@ namespace esphome this->flush(); break; } + case RequestType::POLLING_EXTRA: + { + if (this->log_uart_msg_) PanasonicHelpers::log_uart_hex(UART_LOG_TX, PanasonicCommand::PollingExtraMessage, DATA_MESSAGE_SIZE, ','); + this->write_array(PanasonicCommand::PollingMessage, DATA_MESSAGE_SIZE); + this->flush(); + break; + } }; this->next_request_ = RequestType::NONE; @@ -264,41 +264,62 @@ namespace esphome return -1; } - bool PanasonicHeatpumpComponent::check_response(const std::vector& data) + ResponseType PanasonicHeatpumpComponent::check_response(const std::vector& data) { // Read response message: - // format: 0x71 [payload_length] 0x01 0x10 [[TOP0 - TOP114] ...] 0x00 [checksum] + // format: 0x71 [payload_length] 0x01 [0x10 || 0x21] [[TOP0 - TOP114] ...] 0x00 [checksum] // payload_length: payload_length + 3 = packet_length // checksum: if (sum(all bytes) & 0xFF == 0) ==> valid packet - if (data.empty()) return false; - if (data[0] != 0x71) return false; - if (data[3] != 0x10) return false; + if (data.empty()) return ResponseType::UNKNOWN; + if (data[0] != 0x71) return ResponseType::UNKNOWN; if (data.size() != RESPONSE_MSG_SIZE) { ESP_LOGW(TAG, "Invalid response message length: recieved %d - expected %d", data.size(), RESPONSE_MSG_SIZE); delay(10); // NOLINT - return false; + return ResponseType::UNKNOWN; } + // Verify checksum uint8_t checksum = 0; for (int i = 0; i < data.size(); i++) { checksum += data[i]; } - // checksum = checksum & 0xFF; + // all bytes (including checksum byte) shall be 0x00 if (checksum != 0) { ESP_LOGW(TAG, "Invalid response message: checksum = 0x%02X, last_byte = 0x%02X", checksum, data[202]); delay(10); // NOLINT - return false; + return ResponseType::UNKNOWN; + } + + // Get response type + auto responseType = ResponseType::UNKNOWN; + if (data[3] == 0x10) responseType = ResponseType::DEFAULT; + if (data[3] == 0x21) responseType = ResponseType::EXTRA; + if (responseType == ResponseType::UNKNOWN) + { + ESP_LOGW(TAG, "Unknown response type (4. byte): 0x%02X. Expected 0x10 or 0x21.", data[3]); + delay(10); // NOLINT + return ResponseType::UNKNOWN; + } + + // Check if extra request shall be send + if (responseType == ResponseType::DEFAULT) + { + this->send_extra_request_ = data[199] > 0x02 ? true : false; + } + else + { + this->send_extra_request_ = false; } // Check if the current response is a new response - if (this->last_response_count_ == this->current_response_count_) return false; + if (this->last_response_count_ == this->current_response_count_) return ResponseType::UNKNOWN; this->last_response_count_ = this->current_response_count_; - return true; + return responseType; } void PanasonicHeatpumpComponent::set_command_high_nibble(const uint8_t value, const uint8_t index) diff --git a/components/panasonic_heatpump/panasonic_heatpump.h b/components/panasonic_heatpump/panasonic_heatpump.h index 9a08bd6..18457f2 100644 --- a/components/panasonic_heatpump/panasonic_heatpump.h +++ b/components/panasonic_heatpump/panasonic_heatpump.h @@ -27,17 +27,26 @@ namespace esphome PUBLISH_SELECT, PUBLISH_SWITCH, PUBLISH_CLIMATE, + PUBLISH_EXTRA_SENSOR, SEND_REQUEST, READ_REQUEST, - RESTART_LOOP + RESTART_LOOP, }; enum RequestType : uint8_t { + NONE, + COMMAND, INITIAL, POLLING, - COMMAND, - NONE + POLLING_EXTRA, + }; + + enum ResponseType : uint8_t + { + UNKNOWN, + DEFAULT, + EXTRA, }; class PanasonicHeatpumpEntity @@ -79,6 +88,7 @@ namespace esphome void add_sensor(PanasonicHeatpumpEntity *sensor) { sensors_.push_back(sensor); } void add_switch(PanasonicHeatpumpEntity *switch_) { switches_.push_back(switch_); } void add_text_sensor(PanasonicHeatpumpEntity *text_sensor) { text_sensors_.push_back(text_sensor); } + void add_extra_sensor(PanasonicHeatpumpEntity *sensor) { extra_sensors_.push_back(sensor); } protected: // options variables @@ -95,8 +105,10 @@ namespace esphome uint8_t last_response_count_ { 0 }; bool response_receiving_ { false }; bool request_receiving_ { false }; - RequestType next_request_ { RequestType::INITIAL }; + bool send_extra_request_ { false }; LoopState loop_state_ { LoopState::RESTART_LOOP }; + RequestType next_request_ { RequestType::INITIAL }; + ResponseType current_response_ { ResponseType::UNKNOWN }; // entity vectors std::vector binary_sensors_; std::vector climates_; @@ -105,12 +117,13 @@ namespace esphome std::vector sensors_; std::vector switches_; std::vector text_sensors_; + std::vector extra_sensors_; // uart message functions void read_response(); void send_request(RequestType requestType); void read_request(); - bool check_response(const std::vector& data); + ResponseType check_response(const std::vector& data); }; } // namespace panasonic_heatpump } // namespace esphome diff --git a/components/panasonic_heatpump/sensor/__init__.py b/components/panasonic_heatpump/sensor/__init__.py index 72c4bfa..904ec57 100644 --- a/components/panasonic_heatpump/sensor/__init__.py +++ b/components/panasonic_heatpump/sensor/__init__.py @@ -128,6 +128,13 @@ CONF_TOP136 = "top136" # Bivalent Advanced Start Delay CONF_TOP137 = "top137" # Bivalent Advanced Stop Delay CONF_TOP138 = "top138" # Bivalent Advanced DHW Delay +CONF_XTOP0 = "xtop0" # Heat Power Consumption Extra +CONF_XTOP1 = "xtop1" # Cool Power Consumption Extra +CONF_XTOP2 = "xtop2" # DHW Power Consumption Extra +CONF_XTOP3 = "xtop3" # Heat Power Production Extra +CONF_XTOP4 = "xtop4" # Cool Power Production Extra +CONF_XTOP5 = "xtop5" # DHW Power Production Extra + TYPES = [ CONF_TOP1, CONF_TOP5, @@ -226,6 +233,15 @@ TYPES = [ CONF_TOP138, ] +EXTRA_TYPES = [ + CONF_XTOP0, + CONF_XTOP1, + CONF_XTOP2, + CONF_XTOP3, + CONF_XTOP4, + CONF_XTOP5, +] + PanasonicHeatpumpSensor = panasonic_heatpump_ns.class_("PanasonicHeatpumpSensor", sensor.Sensor, cg.Component) CONFIG_SCHEMA = cv.Schema( @@ -894,6 +910,49 @@ CONFIG_SCHEMA = cv.Schema( state_class=STATE_CLASS_MEASUREMENT, unit_of_measurement = UNIT_MINUTE, ), + + cv.Optional(CONF_XTOP0): sensor.sensor_schema( + PanasonicHeatpumpSensor, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement = UNIT_WATT, + ), + cv.Optional(CONF_XTOP1): sensor.sensor_schema( + PanasonicHeatpumpSensor, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement = UNIT_WATT, + ), + cv.Optional(CONF_XTOP2): sensor.sensor_schema( + PanasonicHeatpumpSensor, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement = UNIT_WATT, + ), + cv.Optional(CONF_XTOP3): sensor.sensor_schema( + PanasonicHeatpumpSensor, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement = UNIT_WATT, + ), + cv.Optional(CONF_XTOP4): sensor.sensor_schema( + PanasonicHeatpumpSensor, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement = UNIT_WATT, + ), + cv.Optional(CONF_XTOP5): sensor.sensor_schema( + PanasonicHeatpumpSensor, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement = UNIT_WATT, + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -906,3 +965,10 @@ async def to_code(config): cg.add(var.set_parent(parent)) cg.add(var.set_id(index)) cg.add(parent.add_sensor(var)) + for index, key in enumerate(EXTRA_TYPES): + if child_config := config.get(key): + var = await sensor.new_sensor(child_config) + await cg.register_component(var, child_config) + cg.add(var.set_parent(parent)) + cg.add(var.set_id(index)) + cg.add(parent.add_extra_sensor(var)) diff --git a/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.cpp b/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.cpp index b133a8e..20586eb 100644 --- a/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.cpp +++ b/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.cpp @@ -590,6 +590,43 @@ namespace esphome if (this->has_state() && this->get_state() == new_state) return; break; } + + case SensorIds::CONF_XTOP0: + { + new_state = PanasonicDecode::getWordMinus1(data, 14); + if (this->has_state() && this->get_state() == new_state) return; + break; + } + case SensorIds::CONF_XTOP1: + { + new_state = PanasonicDecode::getWordMinus1(data, 16); + if (this->has_state() && this->get_state() == new_state) return; + break; + } + case SensorIds::CONF_XTOP2: + { + new_state = PanasonicDecode::getWordMinus1(data, 18); + if (this->has_state() && this->get_state() == new_state) return; + break; + } + case SensorIds::CONF_XTOP3: + { + new_state = PanasonicDecode::getWordMinus1(data, 20); + if (this->has_state() && this->get_state() == new_state) return; + break; + } + case SensorIds::CONF_XTOP4: + { + new_state = PanasonicDecode::getWordMinus1(data, 22); + if (this->has_state() && this->get_state() == new_state) return; + break; + } + case SensorIds::CONF_XTOP5: + { + new_state = PanasonicDecode::getWordMinus1(data, 24); + if (this->has_state() && this->get_state() == new_state) return; + break; + } default: return; }; diff --git a/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.h b/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.h index 637bcfe..c3cea96 100644 --- a/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.h +++ b/components/panasonic_heatpump/sensor/panasonic_heatpump_sensor.h @@ -106,6 +106,13 @@ namespace esphome CONF_TOP136, CONF_TOP137, CONF_TOP138, + + CONF_XTOP0, + CONF_XTOP1, + CONF_XTOP2, + CONF_XTOP3, + CONF_XTOP4, + CONF_XTOP5, }; class PanasonicHeatpumpSensor : public sensor::Sensor, public Component,