diff --git a/components/panasonic_heatpump/README.md b/components/panasonic_heatpump/README.md index 4084656..0d585a0 100644 --- a/components/panasonic_heatpump/README.md +++ b/components/panasonic_heatpump/README.md @@ -546,6 +546,48 @@ select: name: "Set Bivalent Mode" ``` +## Custom Entities (For Advanced Users) + +If you review the [ProtocolByteDecrypt.md](https://github.com/Egyras/HeishaMon/blob/master/ProtocolByteDecrypt.md) file you will find also some TOPs and SETs which are not implemented yet in heishamon. +They are usually marked as TOP (without a number). +The nice part of ESPHome is that it is so highly customizeable. +So if you want some additional TOP or SET entities you can easily create your own. +Under the hood the received uart message from the heatpump is stored in a vector. +Here are 2 examples how to create a sensor and a text_sensor: + +``` +sensor: + - platform: template + name: "Dry concrete target temperature for actual stage" + update_interval: 3s + device_class: temperature + unit_of_measurement: °C + lambda: |- + // get the requried byte + int byte = my_heatpump->getResponseByte(46); + if (byte < 0) return {}; + // convert the byte (see HeishaMon/ProtocolByteDecrypt.md) + // in this case -128 + return byte - 128; + +text_sensor: + - platform: template + name: "DHW capacity (J-series only)" + update_interval: 3s + lambda: |- + // get the requried byte + int byte = my_heatpump->getResponseByte(9); + if (byte < 0) return {}; + // convert the byte (see HeishaMon/ProtocolByteDecrypt.md) + // in this case 3rd and 4th bit (--> b0011 0000) + int state = ((byte >> 4) & 0b11) - 1; + // set text + if (state == 0) return { "Standard" }; + if (state == 1) return { "DHW" }; + // if state is unkown do not update text + return {}; +``` + ## Known Issues When the ESP controller is connected initially to the heatpump, the heatpump may not respond to any request messages. If the CZ-TAW1 is also connected to the ESP controller you will probably see some requests like 31 05 10 01 ... These are initial request messages. If the heatpump is not responding, it may help to turn off and on the power of the heatpump (switching the heatpump off is not enough). After a power on the heatpump should respond to the requests. diff --git a/components/panasonic_heatpump/panasonic_heatpump.cpp b/components/panasonic_heatpump/panasonic_heatpump.cpp index 47ec984..560a272 100644 --- a/components/panasonic_heatpump/panasonic_heatpump.cpp +++ b/components/panasonic_heatpump/panasonic_heatpump.cpp @@ -19,8 +19,15 @@ namespace esphome delay(10); this->check_uart_settings(9600, 1, uart::UART_CONFIG_PARITY_EVEN, 8); // Trigger initial request - this->next_request_ = 0; - this->trigger_request_ = true; + if (this->uart_client_ == nullptr) + { + this->next_request_ = 0; + this->trigger_request_ = true; + } + else + { + this->trigger_request_ = false; + } } void PanasonicHeatpumpComponent::update() @@ -42,49 +49,50 @@ namespace esphome void PanasonicHeatpumpComponent::read_response() { - uint8_t byte; while (this->available()) { // Read byte from heatpump and forward it directly to the client (CZ-TAW1) - this->read_byte(&byte); + this->read_byte(&byte_); if (this->uart_client_ != nullptr) { - this->uart_client_->write_byte(byte); + this->uart_client_->write_byte(byte_); } // Message shall start with 0x31, 0x71 or 0xF1, if not skip this byte if (!this->response_receiving_) { - if (byte != 0x31 && byte != 0x71 && byte != 0xF1) continue; + if (byte_ != 0x31 && byte_ != 0x71 && byte_ != 0xF1) continue; + this->response_message_.clear(); this->response_receiving_ = true; } // Add current byte to message buffer - this->response_message_.push_back(byte); + this->response_message_.push_back(byte_); // 2. byte contains the payload size if (this->response_message_.size() == 2) { - this->response_payload_length_ = byte; + this->response_payload_length_ = byte_; } // Discard message if format is wrong - if ((this->response_message_.size() == 3 || this->response_message_.size() == 4) - && byte != 0x01 && byte != 0x10 && byte != 0x21) + if ((this->response_message_.size() == 3 || + this->response_message_.size() == 4) && + byte_ != 0x01 && byte_ != 0x10 && byte_ != 0x21) { - ESP_LOGW(TAG, "Invalid response message: %d. byte is 0x%02X but expexted is 0x01 or 0x10", - response_message_.size(), byte); - delay(10); - this->response_message_.clear(); this->response_receiving_ = false; + ESP_LOGW(TAG, "Invalid response message: %d. byte is 0x%02X but expexted is 0x01 or 0x10", + response_message_.size(), byte_); + delay(10); continue; } // Check if message is complete - if (this->response_message_.size() > 2 && this->response_message_.size() == this->response_payload_length_ + 3) + if (this->response_message_.size() > 2 && + this->response_message_.size() == this->response_payload_length_ + 3) { - this->log_uart_hex(UART_LOG_RX, this->response_message_, ','); - this->decode_response(this->response_message_); - this->response_message_.clear(); + this->temp_message_ = this->response_message_; this->response_receiving_ = false; + this->log_uart_hex(UART_LOG_RX, this->response_message_, ','); + this->decode_response(this->response_message_);; } } } @@ -107,7 +115,6 @@ namespace esphome { // Probably not necessary but CZ-TAW1 sends this query on boot this->log_uart_hex(UART_LOG_TX, PanasonicCommand::InitialRequest, INIT_REQUEST_SIZE, ','); - this->write_array(PanasonicCommand::InitialRequest, INIT_REQUEST_SIZE); this->flush(); } @@ -123,58 +130,65 @@ namespace esphome { if (this->uart_client_ == nullptr) return; - uint8_t byte; while (this->uart_client_->available()) { // Read byte from client and forward it directly to the heatpump - this->uart_client_->read_byte(&byte); - this->write_byte(byte); + this->uart_client_->read_byte(&byte_); + this->write_byte(byte_); // Message shall start with 0x31, 0x71 or 0xF1, if not skip this byte if (!this->request_receiving_) { - if (byte != 0x31 && byte != 0x71 && byte != 0xF1) continue; + if (byte_ != 0x31 && byte_ != 0x71 && byte_ != 0xF1) continue; + this->request_message_.clear(); this->request_receiving_ = true; } // Add current byte to message buffer - this->request_message_.push_back(byte); + this->request_message_.push_back(byte_); // 2. byte contains the payload size if (this->request_message_.size() == 2) { - this->request_payload_length_ = byte; + this->request_payload_length_ = byte_; } // Discard message if format is wrong - if ((this->request_message_.size() == 3 || this->request_message_.size() == 4) - && byte != 0x01 && byte != 0x10 && byte != 0x21) + if ((this->request_message_.size() == 3 || + this->request_message_.size() == 4) && + byte_ != 0x01 && byte_ != 0x10 && byte_ != 0x21) { - ESP_LOGW(TAG, "Invalid request message: %d. byte is 0x%02X but expexted is 0x01 or 0x10", - request_message_.size(), byte); - delay(10); - this->request_message_.clear(); this->request_receiving_ = false; + ESP_LOGW(TAG, "Invalid request message: %d. byte is 0x%02X but expexted is 0x01 or 0x10", + request_message_.size(), byte_); + delay(10); continue; } // Check if message is complete - if (this->request_message_.size() > 2 && this->request_message_.size() == this->request_payload_length_ + 3) + if (this->request_message_.size() > 2 && + this->request_message_.size() == this->request_payload_length_ + 3) { - this->log_uart_hex(UART_LOG_TX, this->request_message_, ','); - this->request_message_.clear(); this->request_receiving_ = false; + this->log_uart_hex(UART_LOG_TX, this->request_message_, ','); } } } - void PanasonicHeatpumpComponent::log_uart_hex(UartLogDirection direction, const std::vector& data, uint8_t separator) + int PanasonicHeatpumpComponent::getResponseByte(const int index) + { + if (this->response_message_.size() > index) return this->response_message_[index]; + if (this->temp_message_.size() > index) return this->temp_message_[index]; + return -1; + } + + void PanasonicHeatpumpComponent::log_uart_hex(UartLogDirection direction, const std::vector& data, const char separator) { this->log_uart_hex(direction, &data[0], data.size(), separator); } - void PanasonicHeatpumpComponent::log_uart_hex(UartLogDirection direction, const uint8_t* data, size_t length, uint8_t separator) + void PanasonicHeatpumpComponent::log_uart_hex(UartLogDirection direction, const uint8_t* data, const size_t length, const char separator) { if (this->log_uart_msg_ == false) return; - std::string logStr; + std::string logStr = ""; std::string msgDir = direction == UART_LOG_TX ? ">>>" : "<<<"; std::string msgType = direction == UART_LOG_TX ? "request" : "response"; switch(data[0]) @@ -190,11 +204,9 @@ namespace esphome break; }; - logStr = "[" + std::to_string(length) + "]"; - ESP_LOGI(TAG, "%s %s%s", msgDir.c_str(), msgType.c_str(), logStr.c_str()); + ESP_LOGI(TAG, "%s %s[%i]", msgDir.c_str(), msgType.c_str(), length); delay(10); - logStr = ""; char buffer[5]; for (size_t i = 0; i < length; i++) { @@ -246,14 +258,13 @@ namespace esphome this->publish_switch(data); } - void PanasonicHeatpumpComponent::set_command_byte(uint8_t value, uint8_t index) + void PanasonicHeatpumpComponent::set_command_byte(const uint8_t value, const uint8_t index) { if (this->next_request_ == 1) { // initialize the command - command_message_.clear(); - command_message_.insert(this->command_message_.end(), PanasonicCommand::CommandMessage, - PanasonicCommand::CommandMessage + DATA_MESSAGE_SIZE); + command_message_.assign(std::begin(PanasonicCommand::CommandMessage), + std::end(PanasonicCommand::CommandMessage)); } // set command byte command_message_[index] = value; @@ -270,9 +281,8 @@ namespace esphome if (this->next_request_ == 1) { // initialize the command - command_message_.clear(); - command_message_.insert(this->command_message_.end(), PanasonicCommand::CommandMessage, - PanasonicCommand::CommandMessage + DATA_MESSAGE_SIZE); + command_message_.assign(std::begin(PanasonicCommand::CommandMessage), + std::end(PanasonicCommand::CommandMessage)); } // set command bytes for (size_t i = 0; i < data.size(); ++i) diff --git a/components/panasonic_heatpump/panasonic_heatpump.h b/components/panasonic_heatpump/panasonic_heatpump.h index ea3bf10..109da67 100644 --- a/components/panasonic_heatpump/panasonic_heatpump.h +++ b/components/panasonic_heatpump/panasonic_heatpump.h @@ -267,20 +267,23 @@ namespace esphome void set_uart_client(uart::UARTComponent* uart) { this->uart_client_ = uart; } void set_log_uart_msg(bool enable) { this->log_uart_msg_ = enable; } // uart message variables to use in lambda functions - std::vector response_message_; + int getResponseByte(const int index); protected: // options variables uart::UARTComponent* uart_client_ { nullptr }; bool log_uart_msg_ { false }; // uart message variables + std::vector temp_message_; + std::vector response_message_; std::vector request_message_; std::vector command_message_; uint8_t response_payload_length_; uint8_t request_payload_length_; + uint8_t byte_; bool response_receiving_ { false }; bool request_receiving_ { false }; - bool trigger_request_ { true }; + bool trigger_request_ { false }; uint8_t next_request_ { 0 }; // 0 = initial, 1 = polling, 2 = command // uart message functions @@ -288,7 +291,7 @@ namespace esphome void send_request(); void read_request(); void decode_response(const std::vector& data); - void set_command_byte(uint8_t value, uint8_t index); + void set_command_byte(const uint8_t value, const uint8_t index); void set_command_bytes(const std::vector>& data); // sensor and control publish functions void publish_sensor(const std::vector& data); @@ -297,9 +300,9 @@ namespace esphome void publish_number(const std::vector& data); void publish_select(const std::vector& data); void publish_switch(const std::vector& data); - // logger functions - void log_uart_hex(UartLogDirection direction, const std::vector& data, uint8_t separator); - void log_uart_hex(UartLogDirection direction, const uint8_t* data, size_t length, uint8_t separator); + // helper functions + void log_uart_hex(UartLogDirection direction, const std::vector& data, const char separator); + void log_uart_hex(UartLogDirection direction, const uint8_t* data, const size_t length, const char separator); }; } // namespace panasonic_heatpump } // namespace esphome