Added function getResponseByte for lambda use

This commit is contained in:
ElVit 2025-02-21 20:45:37 +01:00
parent 190374dde7
commit 0fa40dab47
3 changed files with 108 additions and 53 deletions

View File

@ -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.

View File

@ -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<uint8_t>& 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<uint8_t>& 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)

View File

@ -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<uint8_t> 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<uint8_t> temp_message_;
std::vector<uint8_t> response_message_;
std::vector<uint8_t> request_message_;
std::vector<uint8_t> 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<uint8_t>& 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<std::tuple<uint8_t, uint8_t>>& data);
// sensor and control publish functions
void publish_sensor(const std::vector<uint8_t>& data);
@ -297,9 +300,9 @@ namespace esphome
void publish_number(const std::vector<uint8_t>& data);
void publish_select(const std::vector<uint8_t>& data);
void publish_switch(const std::vector<uint8_t>& data);
// logger functions
void log_uart_hex(UartLogDirection direction, const std::vector<uint8_t>& 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<uint8_t>& 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