diff --git a/components/samsung_ac/log.cpp b/components/samsung_ac/log.cpp new file mode 100644 index 00000000..c4babdf3 --- /dev/null +++ b/components/samsung_ac/log.cpp @@ -0,0 +1,9 @@ +namespace esphome +{ + namespace samsung_ac + { + bool debug_log_packets = false; + bool debug_log_raw_bytes = false; + + } // namespace samsung_ac +} // namespace esphome \ No newline at end of file diff --git a/components/samsung_ac/log.h b/components/samsung_ac/log.h new file mode 100644 index 00000000..92ccdb24 --- /dev/null +++ b/components/samsung_ac/log.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/core/log.h" +#include "util.h" + +#define LOGV(...) ESP_LOGV(TAG, __VA_ARGS__) +#define LOGD(...) ESP_LOGD(TAG, __VA_ARGS__) +#define LOGI(...) ESP_LOGI(TAG, __VA_ARGS__) +#define LOGW(...) ESP_LOGW(TAG, __VA_ARGS__) +#define LOGE(...) ESP_LOGE(TAG, __VA_ARGS__) +#define LOGC(...) ESP_LOGCONFIG(TAG, __VA_ARGS__) + +#define LOG_STATE(...) LOGI(__VA_ARGS__) +#define LOG_RAW_SEND(inter, ...) ({ if (debug_log_raw_bytes) LOGW("<< +%d: %s", inter, bytes_to_hex(__VA_ARGS__).c_str()); }) +#define LOG_RAW(inter, ...) ({ if (debug_log_raw_bytes) LOGD(">> +%d: %s", inter, bytes_to_hex(__VA_ARGS__).c_str()); }) +#define LOG_RAW_DISCARDED(inter, ...) ({if (debug_log_raw_bytes) LOGV(">> +%d: %s", inter, bytes_to_hex(__VA_ARGS__).c_str()); }) +#define LOG_PACKET_SEND(msg, packet) LOGI("%s: %s", msg, packet.to_string().c_str()) +#define LOG_PACKET_RECV(msg, packet) ({ if (debug_log_packets) LOGD("%s: %s", msg, packet.to_string().c_str()); }) + +namespace esphome +{ + namespace samsung_ac + { + extern bool debug_log_packets; + extern bool debug_log_raw_bytes; + + } // namespace samsung_ac +} // namespace esphome diff --git a/components/samsung_ac/protocol.cpp b/components/samsung_ac/protocol.cpp index 68439bc5..06af4995 100644 --- a/components/samsung_ac/protocol.cpp +++ b/components/samsung_ac/protocol.cpp @@ -1,3 +1,4 @@ +#include #include "esphome/core/log.h" #include "protocol.h" #include "util.h" @@ -8,62 +9,42 @@ namespace esphome { namespace samsung_ac { - bool debug_log_packets = false; - bool debug_log_raw_bytes = false; + uint16_t skip_data(std::vector &data, int from) + { + // Skip over filler data or broken packets + // Example: + // 320037d8fedbff81cb7ffbfd808d00803f008243000082350000805e008031008248ffff801a0082d400000b6a34 f9f6f1f9f9 32000e200002 + // Note that first part is a mangled packet, then regular filler data, then start of a new packet + // and that one new proper packet will continue with the next data read + + // find next value of 0x32, and retry with that one + return std::find(data.begin() + from, data.end(), 0x32) - data.begin(); + } // This functions is designed to run after a new value was added // to the data vector. One by one. - DataResult process_data(std::vector &data, MessageTarget *target) + DecodeResult process_data(std::vector &data, MessageTarget *target) { - if (data.size() > 1500) - { - ESP_LOGV(TAG, "current packat exceeds the size limits: %s", bytes_to_hex(data).c_str()); - return DataResult::Clear; - } + if (*data.begin() != 0x32) + return { DecodeResultType::Discard, skip_data(data, 0) }; - // Check if its a decodeable NonNASA packat - if (data.size() == 7 /* duplicate addr package */ || data.size() == 14 /* generic package */) + auto result = try_decode_non_nasa_packet(data); + if (result.type == DecodeResultType::Processed) { - const auto result = try_decode_non_nasa_packet(data); - if (result == DecodeResult::Ok) - { - if (debug_log_raw_bytes) - { - ESP_LOGW(TAG, "RAW: %s", bytes_to_hex(data).c_str()); - } - - process_non_nasa_packet(target); - return DataResult::Clear; - } + process_non_nasa_packet(target); + return result; } - const auto result = try_decode_nasa_packet(data); - if (result == DecodeResult::SizeDidNotMatch || result == DecodeResult::UnexpectedSize) - return DataResult::Fill; - - if (debug_log_raw_bytes) + result = try_decode_nasa_packet(data); + if (result.type == DecodeResultType::Processed) { - ESP_LOGV(TAG, "RAW: %s", bytes_to_hex(data).c_str()); + process_nasa_packet(target); } - - if (result == DecodeResult::InvalidStartByte) - { - ESP_LOGV(TAG, "invalid start byte: %s", bytes_to_hex(data).c_str()); - return DataResult::Clear; - } - else if (result == DecodeResult::InvalidEndByte) + else if(result.type == DecodeResultType::Discard) { - ESP_LOGV(TAG, "invalid end byte: %s", bytes_to_hex(data).c_str()); - return DataResult::Clear; + return { DecodeResultType::Discard, skip_data(data, 1) }; } - else if (result == DecodeResult::CrcError) - { - // is logge dwithin decoder - return DataResult::Clear; - } - - process_nasa_packet(target); - return DataResult::Clear; + return result; } bool is_nasa_address(const std::string &address) diff --git a/components/samsung_ac/protocol.h b/components/samsung_ac/protocol.h index cac09ee8..e93ea6a5 100644 --- a/components/samsung_ac/protocol.h +++ b/components/samsung_ac/protocol.h @@ -8,17 +8,17 @@ namespace esphome { namespace samsung_ac { - extern bool debug_log_packets; - extern bool debug_log_raw_bytes; + enum class DecodeResultType + { + Fill = 1, + Discard = 2, + Processed = 3 + }; - enum class DecodeResult + struct DecodeResult { - Ok = 0, - InvalidStartByte = 1, - InvalidEndByte = 2, - SizeDidNotMatch = 3, - UnexpectedSize = 4, - CrcError = 5 + DecodeResultType type; + uint16_t bytes; // when Processed }; enum class Mode @@ -63,7 +63,8 @@ namespace esphome { public: virtual uint32_t get_miliseconds() = 0; - virtual void publish_data(std::vector &data) = 0; + virtual void publish_data(uint8_t id, std::vector &&data) = 0; + virtual void ack_data(uint8_t id) = 0; virtual void register_address(const std::string address) = 0; virtual void set_power(const std::string address, bool value) = 0; virtual void set_room_temperature(const std::string address, float value) = 0; @@ -95,13 +96,7 @@ namespace esphome virtual void publish_request(MessageTarget *target, const std::string &address, ProtocolRequest &request) = 0; }; - enum class DataResult - { - Fill = 0, - Clear = 1 - }; - - DataResult process_data(std::vector &data, MessageTarget *target); + DecodeResult process_data(std::vector &data, MessageTarget *target); Protocol *get_protocol(const std::string &address); diff --git a/components/samsung_ac/protocol_nasa.cpp b/components/samsung_ac/protocol_nasa.cpp index 970a916f..41bb00ab 100644 --- a/components/samsung_ac/protocol_nasa.cpp +++ b/components/samsung_ac/protocol_nasa.cpp @@ -1,10 +1,10 @@ #include #include #include -#include "esphome/core/log.h" #include "esphome/core/util.h" #include "esphome/core/hal.h" #include "util.h" +#include "log.h" #include "protocol_nasa.h" #include "debug_mqtt.h" @@ -132,7 +132,7 @@ namespace esphome case Structure: if (capacity != 1) { - ESP_LOGE(TAG, "structure messages can only have one message but is %d", capacity); + LOGE("structure messages can only have one message but is %d", capacity); return set; } Buffer buffer; @@ -145,7 +145,7 @@ namespace esphome set.structure = buffer; break; default: - ESP_LOGE(TAG, "Unkown type"); + LOGE("Unkown type %s", set.type); } return set; @@ -180,7 +180,7 @@ namespace esphome } break; default: - ESP_LOGE(TAG, "Unkown type"); + LOGE("Unkown type"); } } @@ -203,8 +203,6 @@ namespace esphome static int _packetCounter = 0; - std::vector out; - /* class OutgoingPacket { @@ -233,8 +231,6 @@ namespace esphome MessageSet message(messageNumber); message.value = value; packet.messages.push_back(message); - out.push_back(packet); - return packet; } @@ -246,31 +242,41 @@ namespace esphome packet.commad.packetInformation = true; packet.commad.packetType = PacketType::Normal; packet.commad.dataType = dataType; + if (_packetCounter == 0) + _packetCounter++; // skip 0 packet.commad.packetNumber = _packetCounter++; return packet; } DecodeResult Packet::decode(std::vector &data) { - if (data[0] != 0x32) - return DecodeResult::InvalidStartByte; + const uint16_t size = (uint16_t)data[1] << 8 | (uint16_t)data[2]; - if (data.size() < 16 || data.size() > 1500) - return DecodeResult::UnexpectedSize; + if (data.size() < 4) + { + return {DecodeResultType::Fill}; + } + if (size > 1500) + { + LOGW("Packet exceeds size limits: %s", bytes_to_hex(data).c_str()); + return {DecodeResultType::Discard}; + } - int size = (int)data[1] << 8 | (int)data[2]; - if (size + 2 != data.size()) - return DecodeResult::SizeDidNotMatch; + if (size + 2 > data.size()) // need more data + return {DecodeResultType::Fill}; - if (data[data.size() - 1] != 0x34) - return DecodeResult::InvalidEndByte; + if (data[size + 1] != 0x34) + { + LOGW("invalid end byte: %s", bytes_to_hex(data).c_str()); + return {DecodeResultType::Discard}; + } uint16_t crc_actual = crc16(data, 3, size - 4); - uint16_t crc_expected = (int)data[data.size() - 3] << 8 | (int)data[data.size() - 2]; + uint16_t crc_expected = (int)data[size - 1] << 8 | (int)data[size]; if (crc_expected != crc_actual) { - ESP_LOGW(TAG, "NASA: invalid crc - got %d but should be %d: %s", crc_actual, crc_expected, bytes_to_hex(data).c_str()); - return DecodeResult::CrcError; + LOGW("NASA: invalid crc - got %d but should be %d: %s", crc_actual, crc_expected, bytes_to_hex(data).c_str()); + return {DecodeResultType::Discard}; } unsigned int cursor = 3; @@ -295,7 +301,7 @@ namespace esphome cursor += set.size; } - return DecodeResult::Ok; + return {DecodeResultType::Processed, (uint16_t)(size + 2)}; }; std::vector Packet::encode() @@ -422,12 +428,9 @@ namespace esphome if (packet.messages.size() == 0) return; - ESP_LOGW(TAG, "publish packet %s", packet.to_string().c_str()); - - out.push_back(packet); + LOG_PACKET_SEND("Publish packet", packet); - auto data = packet.encode(); - target->publish_data(data); + target->publish_data(packet.commad.packetNumber, packet.encode()); } Mode operation_mode_to_mode(int value) @@ -487,21 +490,21 @@ namespace esphome { if (message.type == MessageSetType::Enum) { - debug_mqtt_publish("samsung_ac/nasa/enum/" + long_to_hex((uint16_t)message.messageNumber), std::to_string(message.value)); + debug_mqtt_publish("samsung_ac/nasa/" + source + "/enum/" + long_to_hex((uint16_t)message.messageNumber), std::to_string(message.value)); } else if (message.type == MessageSetType::Variable) { - debug_mqtt_publish("samsung_ac/nasa/var/" + long_to_hex((uint16_t)message.messageNumber), std::to_string(message.value)); + debug_mqtt_publish("samsung_ac/nasa/" + source + "/var/" + long_to_hex((uint16_t)message.messageNumber), std::to_string(message.value)); } else if (message.type == MessageSetType::LongVariable) { - debug_mqtt_publish("samsung_ac/nasa/var_long/" + long_to_hex((uint16_t)message.messageNumber), std::to_string(message.value)); + debug_mqtt_publish("samsung_ac/nasa/" + source + "/var_long/" + long_to_hex((uint16_t)message.messageNumber), std::to_string(message.value)); } } - if (custom.has_value() && custom.value().find((uint16_t) message.messageNumber) != custom.value().end()) + if (custom.has_value() && custom.value().find((uint16_t)message.messageNumber) != custom.value().end()) { - target->set_custom_sensor(source, (uint16_t) message.messageNumber, (float) message.value); + target->set_custom_sensor(source, (uint16_t)message.messageNumber, (float)message.value); } switch (message.messageNumber) @@ -509,7 +512,7 @@ namespace esphome case MessageNumber::VAR_in_temp_room_f: // unit = 'Celsius' from XML { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_room_f %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_in_temp_room_f %f", source.c_str(), dest.c_str(), temp); target->set_room_temperature(source, temp); return; } @@ -517,31 +520,31 @@ namespace esphome { double temp = (double)message.value / (double)10; // if (value == 1) value = 'waterOutSetTemp'; //action in xml - ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_target_f %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_in_temp_target_f %f", source.c_str(), dest.c_str(), temp); target->set_target_temperature(source, temp); return; } case MessageNumber::ENUM_in_state_humidity_percent: { // XML Enum no value but in Code it adds unit - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_state_humidity_percent %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_state_humidity_percent %li", source.c_str(), dest.c_str(), message.value); return; } case MessageNumber::ENUM_in_operation_power: { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_operation_power %s", source.c_str(), dest.c_str(), message.value == 0 ? "off" : "on"); + LOG_STATE("s:%s d:%s ENUM_in_operation_power %s", source.c_str(), dest.c_str(), message.value == 0 ? "off" : "on"); target->set_power(source, message.value != 0); return; } case MessageNumber::ENUM_in_operation_mode: { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_operation_mode %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_operation_mode %li", source.c_str(), dest.c_str(), message.value); target->set_mode(source, operation_mode_to_mode(message.value)); return; } case MessageNumber::ENUM_in_fan_mode: { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_fan_mode %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_fan_mode %li", source.c_str(), dest.c_str(), message.value); FanMode mode = FanMode::Unknown; if (message.value == 0) mode = FanMode::Auto; @@ -558,36 +561,38 @@ namespace esphome } case MessageNumber::ENUM_in_fan_mode_real: { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_fan_mode_real %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_fan_mode_real %li", source.c_str(), dest.c_str(), message.value); return; } case MessageNumber::ENUM_in_alt_mode: { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_alt_mode %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE(TAG, "s:%s d:%s ENUM_in_alt_mode %li", source.c_str(), dest.c_str(), message.value); target->set_altmode(source, message.value); return; } case MessageNumber::ENUM_in_louver_hl_swing: { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_louver_hl_swing %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_louver_hl_swing %li", source.c_str(), dest.c_str(), message.value); target->set_swing_vertical(source, message.value == 1); return; } case MessageNumber::ENUM_in_louver_lr_swing: { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_louver_lr_swing %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_louver_lr_swing %li", source.c_str(), dest.c_str(), message.value); target->set_swing_horizontal(source, message.value == 1); return; } case MessageNumber::VAR_in_temp_water_tank_f: { ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_water_tank_f %f", source.c_str(), dest.c_str(), message.value); + double temp = (double)message.value / (double)10; + LOG_STATE("s:%s d:%s VAR_in_temp_water_tank_f %f", source.c_str(), dest.c_str(), temp); return; } case MessageNumber::VAR_out_sensor_airout: { - double temp = (double) ((int16_t)message.value) / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_out_sensor_airout %li", source.c_str(), dest.c_str(), message.value); + double temp = (double)((int16_t)message.value) / (double)10; + LOG_STATE("s:%s d:%s VAR_out_sensor_airout %li", source.c_str(), dest.c_str(), message.value); target->set_outdoor_temperature(source, temp); return; } @@ -597,28 +602,28 @@ namespace esphome if ((uint16_t)message.messageNumber == 0x4065) { // ENUM_IN_WATER_HEATER_POWER - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_WATER_HEATER_POWER %s", source.c_str(), dest.c_str(), message.value == 0 ? "off" : "on"); + LOG_STATE("s:%s d:%s ENUM_IN_WATER_HEATER_POWER %s", source.c_str(), dest.c_str(), message.value == 0 ? "off" : "on"); return; } if ((uint16_t)message.messageNumber == 0x4260) { // VAR_IN_FSV_3021 double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_IN_FSV_3021 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_IN_FSV_3021 %f", source.c_str(), dest.c_str(), temp); return; } if ((uint16_t)message.messageNumber == 0x4261) { // VAR_IN_FSV_3022 double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_IN_FSV_3022 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_IN_FSV_3022 %f", source.c_str(), dest.c_str(), temp); return; } if ((uint16_t)message.messageNumber == 0x4262) { // VAR_IN_FSV_3023 double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_IN_FSV_3023 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_IN_FSV_3023 %f", source.c_str(), dest.c_str(), temp); return; } @@ -626,51 +631,57 @@ namespace esphome { // LVAR_OUT_CONTROL_WATTMETER_ALL_UNIT_ACCUM double kwh = (double)message.value / (double)1000; - ESP_LOGW(TAG, "s:%s d:%s LVAR_OUT_CONTROL_WATTMETER_ALL_UNIT_ACCUM %fkwh", source.c_str(), dest.c_str(), kwh); + LOG_STATE("s:%s d:%s LVAR_OUT_CONTROL_WATTMETER_ALL_UNIT_ACCUM %fkwh", source.c_str(), dest.c_str(), kwh); return; } if ((uint16_t)message.messageNumber == 0x8413) { // LVAR_OUT_CONTROL_WATTMETER_1W_1MIN_SUM double value = (double)message.value; - ESP_LOGW(TAG, "s:%s d:%s LVAR_OUT_CONTROL_WATTMETER_1W_1MIN_SUM %f", source.c_str(), dest.c_str(), value); + LOG_STATE("s:%s d:%s LVAR_OUT_CONTROL_WATTMETER_1W_1MIN_SUM %f", source.c_str(), dest.c_str(), value); return; } if ((uint16_t)message.messageNumber == 0x8411) { double value = (double)message.value; - ESP_LOGW(TAG, "s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_1UNIT %f", source.c_str(), dest.c_str(), value); + LOG_STATE("s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_1UNIT %f", source.c_str(), dest.c_str(), value); return; } if ((uint16_t)message.messageNumber == 0x8427) { double value = (double)message.value; - ESP_LOGW(TAG, "s:%s d:%s total produced energy %f", source.c_str(), dest.c_str(), value); + LOG_STATE("s:%s d:%s total produced energy %f", source.c_str(), dest.c_str(), value); return; } if ((uint16_t)message.messageNumber == 0x8426) { double value = (double)message.value; - ESP_LOGW(TAG, "s:%s d:%s actual produced energy %f", source.c_str(), dest.c_str(), value); + LOG_STATE("s:%s d:%s actual produced energy %f", source.c_str(), dest.c_str(), value); return; } if ((uint16_t)message.messageNumber == 0x8415) { double value = (double)message.value; - ESP_LOGW(TAG, "s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_TOTAL_SUM %f", source.c_str(), dest.c_str(), value); + LOG_STATE("s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_TOTAL_SUM %f", source.c_str(), dest.c_str(), value); return; } if ((uint16_t)message.messageNumber == 0x8416) { double value = (double)message.value; - ESP_LOGW(TAG, "s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_TOTAL_SUM_ACCUM %f", source.c_str(), dest.c_str(), value); + LOG_STATE("s:%s d:%s NASA_OUTDOOR_CONTROL_WATTMETER_TOTAL_SUM_ACCUM %f", source.c_str(), dest.c_str(), value); + return; + } + if ((uint16_t)message.messageNumber == 0x24fc) + { + double value = (double)message.value; + LOG_STATE("s:%s d:%s NASA_OUTDOOR_CONTROL_VOLTAGE %f", source.c_str(), dest.c_str(), value); return; } } } } - DecodeResult try_decode_nasa_packet(std::vector data) + DecodeResult try_decode_nasa_packet(std::vector &data) { return packet_.decode(data); } @@ -679,56 +690,48 @@ namespace esphome { const auto source = packet_.sa.to_string(); const auto dest = packet_.da.to_string(); + const auto me = Address::get_my_address().to_string(); target->register_address(source); - if (debug_log_packets) - { - ESP_LOGW(TAG, "MSG: %s", packet_.to_string().c_str()); - } - if (packet_.commad.dataType == DataType::Ack) { - for (int i = 0; i < out.size(); i++) + if (dest == me) { - if (out[i].commad.packetNumber == packet_.commad.packetNumber) - { - ESP_LOGW(TAG, "found %d", out[i].commad.packetNumber); - out.erase(out.begin() + i); - break; - } + LOG_PACKET_SEND("Ack", packet_); + target->ack_data(packet_.commad.packetNumber); } - - ESP_LOGW(TAG, "Ack %s s %d", packet_.to_string().c_str(), out.size()); return; } if (packet_.commad.dataType == DataType::Request) { - ESP_LOGW(TAG, "Request %s", packet_.to_string().c_str()); + LOG_PACKET_RECV("Request", packet_); return; } if (packet_.commad.dataType == DataType::Response) { - ESP_LOGW(TAG, "Response %s", packet_.to_string().c_str()); + LOG_PACKET_RECV("Response", packet_); return; } if (packet_.commad.dataType == DataType::Write) { - ESP_LOGW(TAG, "Write %s", packet_.to_string().c_str()); + LOG_PACKET_RECV("Write", packet_); return; } if (packet_.commad.dataType == DataType::Nack) { - ESP_LOGW(TAG, "Nack %s", packet_.to_string().c_str()); + LOG_PACKET_RECV("Nack", packet_); return; } if (packet_.commad.dataType == DataType::Read) { - ESP_LOGW(TAG, "Read %s", packet_.to_string().c_str()); + LOG_PACKET_RECV("Read", packet_); return; } + LOG_PACKET_RECV("RECV", packet_); + if (packet_.commad.dataType != DataType::Notification) return; @@ -747,42 +750,42 @@ namespace esphome return; if (((uint16_t)message.messageNumber) == 0x4003) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_OPERATION_VENT_POWER %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_OPERATION_VENT_POWER %li", source.c_str(), dest.c_str(), message.value); return; } if (((uint16_t)message.messageNumber) == 0x4004) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_OPERATION_VENT_MODE %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_OPERATION_VENT_MODE %li", source.c_str(), dest.c_str(), message.value); return; } if (((uint16_t)message.messageNumber) == 0x4011) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_LOUVER_HL_SWING %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_LOUVER_HL_SWING %li", source.c_str(), dest.c_str(), message.value); return; } if (((uint16_t)message.messageNumber) == 0x4012) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_louver_hl_part_swing %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_louver_hl_part_swing %li", source.c_str(), dest.c_str(), message.value); return; } if (((uint16_t)message.messageNumber) == 0x4060) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_ALTERNATIVE_MODE %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_ALTERNATIVE_MODE %li", source.c_str(), dest.c_str(), message.value); return; } if (((uint16_t)message.messageNumber) == 0x406E) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_QUIET_MODE %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_QUIET_MODE %li", source.c_str(), dest.c_str(), message.value); return; } if (((uint16_t)message.messageNumber) == 0x4119) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_OPERATION_POWER_ZONE1 %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_OPERATION_POWER_ZONE1 %li", source.c_str(), dest.c_str(), message.value); return; } if (((uint16_t)message.messageNumber) == 0x411E) { - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_OPERATION_POWER_ZONE2 %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_OPERATION_POWER_ZONE2 %li", source.c_str(), dest.c_str(), message.value); return; } @@ -793,13 +796,13 @@ namespace esphome case 0x4002: // ENUM_in_operation_mode_real { // Todo Map - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_operation_mode_real %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_operation_mode_real %li", source.c_str(), dest.c_str(), message.value); return; } case 0x4008: // ENUM_in_fan_vent_mode { - ESP_LOGW(TAG, "s:%s d:%s ENUM_in_fan_vent_mode %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_in_fan_vent_mode %li", source.c_str(), dest.c_str(), message.value); // fan_vent_mode_to_fanmode(); return; } @@ -815,7 +818,7 @@ namespace esphome default: return undefined; */ - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_LOUVER_HL_SWING %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_LOUVER_HL_SWING %li", source.c_str(), dest.c_str(), message.value); return; } @@ -823,35 +826,35 @@ namespace esphome { // Todo Map - ESP_LOGW(TAG, "s:%s d:%s ENUM_IN_LOUVER_HL_SWING %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_IN_LOUVER_HL_SWING %li", source.c_str(), dest.c_str(), message.value); return; } case 0x4205: // VAR_in_temp_eva_in_f unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_eva_in_f %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_in_temp_eva_in_f %f", source.c_str(), dest.c_str(), temp); return; } case 0x4206: // VAR_in_temp_eva_out_f unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_in_temp_eva_out_f %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_in_temp_eva_out_f %f", source.c_str(), dest.c_str(), temp); return; } case 0x4211: // VAR_in_capacity_request unit = 'kW' { double temp = (double)message.value / (double)8.6; - ESP_LOGW(TAG, "s:%s d:%s VAR_in_capacity_request %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_in_capacity_request %f", source.c_str(), dest.c_str(), temp); return; } case 0x8001: // ENUM_out_operation_odu_mode { // Todo Map - ESP_LOGW(TAG, "s:%s d:%s ENUM_out_operation_odu_mode %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_out_operation_odu_mode %li", source.c_str(), dest.c_str(), message.value); return; } @@ -859,111 +862,111 @@ namespace esphome { //['Undefined', 'Cool', 'Heat', 'CoolMain', 'HeatMain']; // Todo Map - ESP_LOGW(TAG, "s:%s d:%s ENUM_out_operation_heatcool %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_out_operation_heatcool %li", source.c_str(), dest.c_str(), message.value); return; } case 0x801a: // ENUM_out_load_4way { - ESP_LOGW(TAG, "s:%s d:%s ENUM_out_load_4way %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s ENUM_out_load_4way %li", source.c_str(), dest.c_str(), message.value); return; } case 0x8235: // VAR_out_error_code { - ESP_LOGW(TAG, "s:%s d:%s VAR_out_error_code %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s VAR_out_error_code %li", source.c_str(), dest.c_str(), message.value); return; } case 0x8261: // VAR_OUT_SENSOR_PIPEIN3 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEIN3 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEIN3 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8262: // VAR_OUT_SENSOR_PIPEIN4 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEIN4 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEIN4 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8263: // VAR_OUT_SENSOR_PIPEIN5 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEIN5 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEIN5 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8264: // VAR_OUT_SENSOR_PIPEOUT1 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEOUT1 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEOUT1 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8265: // VAR_OUT_SENSOR_PIPEOUT2 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEOUT2 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEOUT2 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8266: // VAR_OUT_SENSOR_PIPEOUT3 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEOUT3 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEOUT3 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8267: // VAR_OUT_SENSOR_PIPEOUT4 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEOUT4 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEOUT4 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8268: // VAR_OUT_SENSOR_PIPEOUT5 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_SENSOR_PIPEOUT5 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_OUT_SENSOR_PIPEOUT5 %f", source.c_str(), dest.c_str(), temp); return; } case 0x8274: // VAR_out_control_order_cfreq_comp2 { - ESP_LOGW(TAG, "s:%s d:%s VAR_out_control_order_cfreq_comp2 %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s VAR_out_control_order_cfreq_comp2 %li", source.c_str(), dest.c_str(), message.value); return; } case 0x8275: // VAR_out_control_target_cfreq_comp2 { - ESP_LOGW(TAG, "s:%s d:%s VAR_out_control_target_cfreq_comp2 %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s VAR_out_control_target_cfreq_comp2 %li", source.c_str(), dest.c_str(), message.value); return; } case 0x82bc: // VAR_OUT_PROJECT_CODE { - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_PROJECT_CODE %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s VAR_OUT_PROJECT_CODE %li", source.c_str(), dest.c_str(), message.value); return; } case 0x82e3: // VAR_OUT_PRODUCT_OPTION_CAPA { - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_PRODUCT_OPTION_CAPA %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s VAR_OUT_PRODUCT_OPTION_CAPA %li", source.c_str(), dest.c_str(), message.value); return; } case 0x8280: // VAR_out_sensor_top1 unit = 'Celsius' { double temp = (double)message.value / (double)10; - ESP_LOGW(TAG, "s:%s d:%s VAR_out_sensor_top1 %f", source.c_str(), dest.c_str(), temp); + LOG_STATE("s:%s d:%s VAR_out_sensor_top1 %f", source.c_str(), dest.c_str(), temp); return; } case 0x82db: // VAR_OUT_PHASE_CURRENT { - ESP_LOGW(TAG, "s:%s d:%s VAR_OUT_PHASE_CURRENT %li", source.c_str(), dest.c_str(), message.value); + LOG_STATE("s:%s d:%s VAR_OUT_PHASE_CURRENT %li", source.c_str(), dest.c_str(), message.value); return; } @@ -1105,7 +1108,7 @@ namespace esphome case 0x2401: case 0x24fc: { - // ESP_LOGW(TAG, "s:%s d:%s Todo %s %li", source.c_str(), dest.c_str(), long_to_hex((int)message.messageNumber).c_str(), message.value); + // LOG_STATE("s:%s d:%s Todo %s %li", source.c_str(), dest.c_str(), long_to_hex((int)message.messageNumber).c_str(), message.value); return; // Todo } @@ -1119,7 +1122,7 @@ namespace esphome case 0x42d2: // VAR_IN_DUST_SENSOR_PM2_5_VALUE case 0x42d3: // VAR_IN_DUST_SENSOR_PM1_0_VALUE { - // ESP_LOGW(TAG, "s:%s d:%s Ignore %s %li", source.c_str(), dest.c_str(), long_to_hex((int)message.messageNumber).c_str(), message.value); + // LOG_STATE("s:%s d:%s Ignore %s %li", source.c_str(), dest.c_str(), long_to_hex((int)message.messageNumber).c_str(), message.value); return; // Ingore cause not important } @@ -1167,12 +1170,12 @@ namespace esphome case 0x4204: case 0x4006: { - // ESP_LOGW(TAG, "s:%s d:%s NoMap %s %li", source.c_str(), dest.c_str(), long_to_hex((int)message.messageNumber).c_str(), message.value); + // LOG_STATE("s:%s d:%s NoMap %s %li", source.c_str(), dest.c_str(), long_to_hex((int)message.messageNumber).c_str(), message.value); return; // message types witch have no mapping in xml } default: - ESP_LOGW(TAG, "s:%s d:%s !! unknown %s", source.c_str(), dest.c_str(), message.to_string().c_str()); + LOG_STATE("s:%s d:%s !! unknown %s", source.c_str(), dest.c_str(), message.to_string().c_str()); } return; } diff --git a/components/samsung_ac/protocol_nasa.h b/components/samsung_ac/protocol_nasa.h index 1440ddbb..24661a1d 100644 --- a/components/samsung_ac/protocol_nasa.h +++ b/components/samsung_ac/protocol_nasa.h @@ -161,7 +161,7 @@ namespace esphome std::string to_string(); }; - DecodeResult try_decode_nasa_packet(std::vector data); + DecodeResult try_decode_nasa_packet(std::vector &data); void process_nasa_packet(MessageTarget *target); class NasaProtocol : public Protocol diff --git a/components/samsung_ac/protocol_non_nasa.cpp b/components/samsung_ac/protocol_non_nasa.cpp index 48cd1391..d667409d 100644 --- a/components/samsung_ac/protocol_non_nasa.cpp +++ b/components/samsung_ac/protocol_non_nasa.cpp @@ -2,9 +2,9 @@ #include #include #include -#include "esphome/core/log.h" #include "esphome/core/hal.h" #include "util.h" +#include "log.h" #include "protocol_non_nasa.h" std::map last_command20s_; @@ -155,21 +155,18 @@ namespace esphome DecodeResult NonNasaDataPacket::decode(std::vector &data) { - if (data[0] != 0x32) - return DecodeResult::InvalidStartByte; + if (data.size() != 7 && data.size() != 14) + return { DecodeResultType::Discard }; if (data[data.size() - 1] != 0x34) - return DecodeResult::InvalidEndByte; - - if (data.size() != 7 && data.size() != 14) - return DecodeResult::UnexpectedSize; + return { DecodeResultType::Discard }; auto crc_expected = build_checksum(data); auto crc_actual = data[data.size() - 2]; if (crc_actual != build_checksum(data)) { - ESP_LOGW(TAG, "NonNASA: invalid crc - got %d but should be %d: %s", crc_actual, crc_expected, bytes_to_hex(data).c_str()); - return DecodeResult::CrcError; + LOGW("NonNASA: invalid crc - got %d but should be %d: %s", crc_actual, crc_expected, bytes_to_hex(data).c_str()); + return { DecodeResultType::Discard }; } src = long_to_hex(data[1]); @@ -192,7 +189,7 @@ namespace esphome if (command20.wind_direction == (NonNasaWindDirection)0) command20.wind_direction = NonNasaWindDirection::Stop; - return DecodeResult::Ok; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } case NonNasaCommand::CmdC0: // outdoor unit data { @@ -204,17 +201,17 @@ namespace esphome commandC0.outdoor_unit_outdoor_temp_c = data[8] - 55; commandC0.outdoor_unit_discharge_temp_c = data[10] - 55; commandC0.outdoor_unit_condenser_mid_temp_c = data[11] - 55; - return DecodeResult::Ok; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } case NonNasaCommand::CmdC1: // outdoor unit data { commandC1.outdoor_unit_sump_temp_c = data[8] - 55; - return DecodeResult::Ok; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } case NonNasaCommand::CmdC6: { commandC6.control_status = data[4]; - return DecodeResult::Ok; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } case NonNasaCommand::CmdF0: // outdoor unit data { @@ -228,7 +225,7 @@ namespace esphome commandF0.inverter_current_frequency_hz = data[7]; commandF0.outdoor_unit_bldc_fan = data[8] & 0b00000011; // not sure if correct, i have no ou with BLDC-fan commandF0.outdoor_unit_error_code = data[10]; - return DecodeResult::Ok; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } case NonNasaCommand::CmdF1: // outdoor unit eev-values { @@ -236,7 +233,7 @@ namespace esphome commandF1.outdoor_unit_EEV_B = (data[6] * 256) + data[7]; commandF1.outdoor_unit_EEV_C = (data[8] * 256) + data[9]; commandF1.outdoor_unit_EEV_D = (data[10] * 256) + data[11]; - return DecodeResult::Ok; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } case NonNasaCommand::CmdF3: // power consumption { @@ -247,17 +244,17 @@ namespace esphome // DC-current to the inverter of outdoor-unit in A commandF3.inverter_current_a = (float)data[8] / 10; // voltage of the DC-link to inverter in V - commandF3.inverter_voltage_v = (float)data[9] * 2; + commandF3.inverter_voltage_v = (float)data[9] * 2; // Power consumption of the outdoo unit inverter in W - commandF3.inverter_power_w = commandF3.inverter_current_a * commandF3.inverter_voltage_v; - return DecodeResult::Ok; + commandF3.inverter_power_w = commandF3.inverter_current_a * commandF3.inverter_voltage_v; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } default: { commandRaw.length = data.size() - 4 - 1; auto begin = data.begin() + 4; std::copy(begin, begin + commandRaw.length, commandRaw.data); - return DecodeResult::Ok; + return { DecodeResultType::Processed, (uint16_t) data.size() }; } } } @@ -415,15 +412,15 @@ namespace esphome if (request.alt_mode) { - ESP_LOGW(TAG, "change altmode is currently not implemented"); + LOGW("change altmode is currently not implemented"); } if (request.swing_mode) { - ESP_LOGW(TAG, "change swingmode is currently not implemented"); + LOGW("change swingmode is currently not implemented"); } - nonnasa_requests.push(req); + nonnasa_requests.push(std::move(req)); } Mode nonnasa_mode_to_mode(NonNasaMode value) @@ -463,7 +460,7 @@ namespace esphome } } - DecodeResult try_decode_non_nasa_packet(std::vector data) + DecodeResult try_decode_non_nasa_packet(std::vector &data) { return nonpacket_.decode(data); } @@ -473,18 +470,16 @@ namespace esphome while (nonnasa_requests.size() > 0) { delay(delay_ms); - auto data = nonnasa_requests.front().encode(); - target->publish_data(data); + auto packet = &nonnasa_requests.front(); + //LOG_PACKET_SEND("SEND", packet); + target->publish_data(0, packet->encode()); nonnasa_requests.pop(); } } void process_non_nasa_packet(MessageTarget *target) { - if (debug_log_packets) - { - ESP_LOGW(TAG, "MSG: %s", nonpacket_.to_string().c_str()); - } + LOG_PACKET_RECV("RECV", nonpacket_); target->register_address(nonpacket_.src); diff --git a/components/samsung_ac/protocol_non_nasa.h b/components/samsung_ac/protocol_non_nasa.h index e89c9c46..9c9f86f6 100644 --- a/components/samsung_ac/protocol_non_nasa.h +++ b/components/samsung_ac/protocol_non_nasa.h @@ -188,7 +188,7 @@ namespace esphome static NonNasaRequest create(std::string dst_address); }; - DecodeResult try_decode_non_nasa_packet(std::vector data); + DecodeResult try_decode_non_nasa_packet(std::vector &data); void process_non_nasa_packet(MessageTarget *target); class NonNasaProtocol : public Protocol diff --git a/components/samsung_ac/samsung_ac.cpp b/components/samsung_ac/samsung_ac.cpp index daf8eb1b..114ca63a 100644 --- a/components/samsung_ac/samsung_ac.cpp +++ b/components/samsung_ac/samsung_ac.cpp @@ -1,8 +1,10 @@ -#include "esphome/core/log.h" #include "samsung_ac.h" #include "debug_mqtt.h" #include "util.h" +#include "log.h" +#include #include +#include namespace esphome { @@ -10,19 +12,16 @@ namespace esphome { void Samsung_AC::setup() { - ESP_LOGW(TAG, "setup"); } void Samsung_AC::update() { - ESP_LOGW(TAG, "update"); - debug_mqtt_connect(debug_mqtt_host, debug_mqtt_port, debug_mqtt_username, debug_mqtt_password); // Waiting for first update before beginning processing data if (data_processing_init) { - ESP_LOGCONFIG(TAG, "Data Processing starting"); + LOGC("Data Processing starting"); data_processing_init = false; } @@ -31,7 +30,7 @@ namespace esphome { devices += devices.length() > 0 ? ", " + pair.second->address : pair.second->address; } - ESP_LOGCONFIG(TAG, "Configured devices: %s", devices.c_str()); + LOGC("Configured devices: %s", devices.c_str()); std::string knownIndoor = ""; std::string knownOutdoor = ""; @@ -51,18 +50,18 @@ namespace esphome break; } } - ESP_LOGCONFIG(TAG, "Discovered devices:"); - ESP_LOGCONFIG(TAG, " Outdoor: %s", (knownOutdoor.length() == 0 ? "-" : knownOutdoor.c_str())); - ESP_LOGCONFIG(TAG, " Indoor: %s", (knownIndoor.length() == 0 ? "-" : knownIndoor.c_str())); + LOGC("Discovered devices:"); + LOGC(" Outdoor: %s", (knownOutdoor.length() == 0 ? "-" : knownOutdoor.c_str())); + LOGC(" Indoor: %s", (knownIndoor.length() == 0 ? "-" : knownIndoor.c_str())); if (knownOther.length() > 0) - ESP_LOGCONFIG(TAG, " Other: %s", knownOther.c_str()); + LOGC(" Other: %s", knownOther.c_str()); } void Samsung_AC::register_device(Samsung_AC_Device *device) { if (find_device(device->address) != nullptr) { - ESP_LOGW(TAG, "There is already and device for address %s registered.", device->address.c_str()); + LOGW("There is already and device for address %s registered.", device->address.c_str()); return; } @@ -73,11 +72,37 @@ namespace esphome { } - void Samsung_AC::publish_data(std::vector &data) + void Samsung_AC::publish_data(uint8_t id, std::vector &&data) + { + const uint32_t now = millis(); + + if (id == 0) + { + LOG_RAW_SEND(now-last_transmission_, data); + last_transmission_ = now; + this->write_array(data); + this->flush(); + return; + } + + OutgoingData outData; + outData.id = id; + outData.data = std::move(data); + outData.nextRetry = 0; + outData.retries = 0; + outData.timeout = now + sendTimeout; + send_queue_.push_back(std::move(outData)); + } + + void Samsung_AC::ack_data(uint8_t id) { - ESP_LOGW(TAG, "write %s", bytes_to_hex(data).c_str()); - this->write_array(data); - this->flush(); + if (!send_queue_.empty()) + { + auto senddata = &send_queue_.front(); + if (senddata->id == id) { + send_queue_.pop_front(); + } + } } void Samsung_AC::loop() @@ -85,42 +110,86 @@ namespace esphome if (data_processing_init) return; - const uint32_t now = millis(); - if (data_.size() > 0 && (now - last_transmission_ >= 500)) - { - ESP_LOGW(TAG, "Last transmission too long ago. Reset RX index."); - data_.clear(); - } + // if more data is expected, do not allow anything to be written + if (!read_data()) + return; // If there is no data we use the time to send - if (!available()) + write_data(); + } + + bool Samsung_AC::read_data() + { + // read as long as there is anything to read + while (available()) { - if (send_queue_.size() > 0) + uint8_t c; + if (read_byte(&c)) { - auto senddata = send_queue_.front(); - publish_data(senddata); - send_queue_.pop(); + data_.push_back(c); } + } + + if (data_.empty()) + return true; - return; // nothing in uart-input-buffer, end here + const uint32_t now = millis(); + + auto result = process_data(data_, this); + if (result.type == DecodeResultType::Fill) + return false; + + if (result.type == DecodeResultType::Discard) + { + // collect more so that we can log all discarded bytes at once, but don't wait for too long + if (result.bytes == data_.size() && now-last_transmission_ < 1000) + return false; + LOG_RAW_DISCARDED(now-last_transmission_, data_, 0, result.bytes); + } + else + { + LOG_RAW(now-last_transmission_, data_, 0, result.bytes); } - last_transmission_ = now; - while (available()) + if (result.bytes == data_.size()) + data_.clear(); + else { - uint8_t c; - if (!read_byte(&c)) - continue; - if (data_.size() == 0 && c != 0x32) - continue; // skip until start-byte found + std::move(data_.begin() + result.bytes, data_.end(), data_.begin()); + data_.resize(data_.size() - result.bytes); + } + last_transmission_ = now; + return false; + } - data_.push_back(c); + void Samsung_AC::write_data() + { + if (send_queue_.empty()) + return; - if (process_data(data_, this) == DataResult::Clear) - { - data_.clear(); - break; // wait for next loop + auto senddata = &send_queue_.front(); + + const uint32_t now = millis(); + if (senddata->timeout <= now && senddata->retries >= minRetries) { + LOGE("Packet sending timeout %d after %d retries", senddata->id, senddata->retries); + send_queue_.pop_front(); + return; + } + + if (now-last_transmission_ > silenceInterval && senddata->nextRetry < now) + { + if (senddata->nextRetry > 0){ + LOGW("Retry sending packet %d", senddata->id); + senddata->retries++; } + + LOG_RAW_SEND(now-last_transmission_, senddata->data); + + last_transmission_ = now; + senddata->nextRetry = now + retryInterval; + + this->write_array(senddata->data); + this->flush(); } } diff --git a/components/samsung_ac/samsung_ac.h b/components/samsung_ac/samsung_ac.h index a3baabd3..8ab32805 100644 --- a/components/samsung_ac/samsung_ac.h +++ b/components/samsung_ac/samsung_ac.h @@ -8,6 +8,7 @@ #include "esphome/components/uart/uart.h" #include "samsung_ac_device.h" #include "protocol.h" +#include "log.h" namespace esphome { @@ -16,6 +17,27 @@ namespace esphome class NasaProtocol; class Samsung_AC_Device; + // time to wait since last wire activity before sending + const uint16_t silenceInterval = 100; + + // minimum time before a retry attempt + const uint16_t retryInterval = 500; + + // minimum number of retries, even beyond timeout + const uint8_t minRetries = 1; + + // maximum time to wait before discarding command + const uint16_t sendTimeout = 4000; + + struct OutgoingData + { + uint8_t id; + std::vector data; + uint32_t nextRetry; + uint32_t timeout; + uint8_t retries; + }; + class Samsung_AC : public PollingComponent, public uart::UARTDevice, public MessageTarget @@ -59,7 +81,9 @@ namespace esphome return millis(); } - void /*MessageTarget::*/ publish_data(std::vector &data); + void /*MessageTarget::*/ publish_data(uint8_t id, std::vector &&data); + + void /*MessageTarget::*/ ack_data(uint8_t id); void /*MessageTarget::*/ set_room_temperature(const std::string address, float value) override { @@ -153,8 +177,12 @@ namespace esphome std::map devices_; std::set addresses_; - std::queue> send_queue_; + std::deque send_queue_; std::vector data_; + + bool read_data(); + void write_data(); + uint32_t last_transmission_{0}; bool data_processing_init = true; diff --git a/components/samsung_ac/util.cpp b/components/samsung_ac/util.cpp index acaa7ad5..90571f5e 100644 --- a/components/samsung_ac/util.cpp +++ b/components/samsung_ac/util.cpp @@ -17,9 +17,14 @@ namespace esphome } std::string bytes_to_hex(const std::vector &data) + { + return bytes_to_hex(data, 0, data.size()); + } + + std::string bytes_to_hex(const std::vector &data, uint16_t start, uint16_t end) { std::string str; - for (int i = 0; i < data.size(); i++) + for (int i = start; i < end; i++) { str += long_to_hex(data[i]); } diff --git a/components/samsung_ac/util.h b/components/samsung_ac/util.h index fbe8fba4..f253569a 100644 --- a/components/samsung_ac/util.h +++ b/components/samsung_ac/util.h @@ -15,6 +15,7 @@ namespace esphome std::string long_to_hex(long number); int hex_to_int(const std::string &hex); + std::string bytes_to_hex(const std::vector &data, uint16_t start, uint16_t end); std::string bytes_to_hex(const std::vector &data); std::vector hex_to_bytes(const std::string &hex); void print_bits_8(uint8_t value);