diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index b92cc3be9f..bafd5273da 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -38,7 +38,6 @@ void Pipsolar::loop() { } if (this->state_ == STATE_COMMAND_COMPLETE) { if (this->check_incoming_length_(4)) { - ESP_LOGD(TAG, "response length for command OK"); if (this->check_incoming_crc_()) { // crc ok if (this->read_buffer_[1] == 'A' && this->read_buffer_[2] == 'C' && this->read_buffer_[3] == 'K') { @@ -49,15 +48,15 @@ void Pipsolar::loop() { this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; this->state_ = STATE_IDLE; - } else { // crc failed + // no log message necessary, check_incoming_crc_() logs this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; this->state_ = STATE_IDLE; } } else { - ESP_LOGD(TAG, "response length for command %s not OK: with length %zu", + ESP_LOGD(TAG, "command %s response length not OK: with length %zu", this->command_queue_[this->command_queue_position_].c_str(), this->read_pos_); this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; @@ -66,46 +65,10 @@ void Pipsolar::loop() { } if (this->state_ == STATE_POLL_CHECKED) { - switch (this->enabled_polling_commands_[this->last_polling_command_].identifier) { - case POLLING_QPIRI: - ESP_LOGD(TAG, "Decode QPIRI"); - handle_qpiri_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QPIGS: - ESP_LOGD(TAG, "Decode QPIGS"); - handle_qpigs_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QMOD: - ESP_LOGD(TAG, "Decode QMOD"); - handle_qmod_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QFLAG: - ESP_LOGD(TAG, "Decode QFLAG"); - handle_qflag_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QPIWS: - ESP_LOGD(TAG, "Decode QPIWS"); - handle_qpiws_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QT: - ESP_LOGD(TAG, "Decode QT"); - handle_qt_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - case POLLING_QMN: - ESP_LOGD(TAG, "Decode QMN"); - handle_qmn_((const char *) this->read_buffer_); - this->state_ = STATE_IDLE; - break; - default: - this->state_ = STATE_IDLE; - break; - } + ESP_LOGD(TAG, "poll %s decode", this->enabled_polling_commands_[this->last_polling_command_].command); + this->handle_poll_response_(this->enabled_polling_commands_[this->last_polling_command_].identifier, + (const char *) this->read_buffer_); + this->state_ = STATE_IDLE; return; } @@ -113,6 +76,8 @@ void Pipsolar::loop() { if (this->check_incoming_crc_()) { if (this->read_buffer_[0] == '(' && this->read_buffer_[1] == 'N' && this->read_buffer_[2] == 'A' && this->read_buffer_[3] == 'K') { + ESP_LOGD(TAG, "poll %s NACK", this->enabled_polling_commands_[this->last_polling_command_].command); + this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier); this->state_ = STATE_IDLE; return; } @@ -121,6 +86,9 @@ void Pipsolar::loop() { this->state_ = STATE_POLL_CHECKED; return; } else { + // crc failed + // no log message necessary, check_incoming_crc_() logs + this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier); this->state_ = STATE_IDLE; } } @@ -158,21 +126,19 @@ void Pipsolar::loop() { // command timeout const char *command = this->command_queue_[this->command_queue_position_].c_str(); this->command_start_millis_ = millis(); - ESP_LOGD(TAG, "timeout command from queue: %s", command); + ESP_LOGD(TAG, "command %s timeout", command); this->command_queue_[this->command_queue_position_] = std::string(""); this->command_queue_position_ = (command_queue_position_ + 1) % COMMAND_QUEUE_LENGTH; this->state_ = STATE_IDLE; return; - } else { } } if (this->state_ == STATE_POLL) { if (millis() - this->command_start_millis_ > esphome::pipsolar::Pipsolar::COMMAND_TIMEOUT) { // command timeout - ESP_LOGD(TAG, "timeout command to poll: %s", - this->enabled_polling_commands_[this->last_polling_command_].command); + ESP_LOGD(TAG, "poll %s timeout", this->enabled_polling_commands_[this->last_polling_command_].command); + this->handle_poll_error_(this->enabled_polling_commands_[this->last_polling_command_].identifier); this->state_ = STATE_IDLE; - } else { } } } @@ -187,7 +153,6 @@ uint8_t Pipsolar::check_incoming_length_(uint8_t length) { uint8_t Pipsolar::check_incoming_crc_() { uint16_t crc16; crc16 = this->pipsolar_crc_(read_buffer_, read_pos_ - 3); - ESP_LOGD(TAG, "checking crc on incoming message"); if (((uint8_t) ((crc16) >> 8)) == read_buffer_[read_pos_ - 3] && ((uint8_t) ((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) { ESP_LOGD(TAG, "CRC OK"); @@ -253,7 +218,7 @@ bool Pipsolar::send_next_poll_() { this->write(((uint8_t) ((crc16) &0xff))); // lowbyte // end Byte this->write(0x0D); - ESP_LOGD(TAG, "Sending polling command : %s with length %d", + ESP_LOGD(TAG, "Sending polling command: %s with length %d", this->enabled_polling_commands_[this->last_polling_command_].command, this->enabled_polling_commands_[this->last_polling_command_].length); return true; @@ -274,6 +239,38 @@ void Pipsolar::queue_command(const std::string &command) { ESP_LOGD(TAG, "Command queue full dropping command: %s", command.c_str()); } +void Pipsolar::handle_poll_response_(ENUMPollingCommand polling_command, const char *message) { + switch (polling_command) { + case POLLING_QPIRI: + handle_qpiri_(message); + break; + case POLLING_QPIGS: + handle_qpigs_(message); + break; + case POLLING_QMOD: + handle_qmod_(message); + break; + case POLLING_QFLAG: + handle_qflag_(message); + break; + case POLLING_QPIWS: + handle_qpiws_(message); + break; + case POLLING_QT: + handle_qt_(message); + break; + case POLLING_QMN: + handle_qmn_(message); + break; + default: + break; + } +} +void Pipsolar::handle_poll_error_(ENUMPollingCommand polling_command) { + // handlers are designed in a way that an empty message sets all sensors to unknown + this->handle_poll_response_(polling_command, ""); +} + void Pipsolar::handle_qpiri_(const char *message) { if (this->last_qpiri_) { this->last_qpiri_->publish_state(message); diff --git a/esphome/components/pipsolar/pipsolar.h b/esphome/components/pipsolar/pipsolar.h index 40056bac9d..beae67a4e0 100644 --- a/esphome/components/pipsolar/pipsolar.h +++ b/esphome/components/pipsolar/pipsolar.h @@ -204,6 +204,9 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { bool send_next_command_(); bool send_next_poll_(); + void handle_poll_response_(ENUMPollingCommand polling_command, const char *message); + void handle_poll_error_(ENUMPollingCommand polling_command); + // these handlers are designed in a way that an empty message sets all sensors to unknown void handle_qpiri_(const char *message); void handle_qpigs_(const char *message); void handle_qmod_(const char *message); diff --git a/esphome/components/pipsolar/sensor/__init__.py b/esphome/components/pipsolar/sensor/__init__.py index 929865b480..d08a877b55 100644 --- a/esphome/components/pipsolar/sensor/__init__.py +++ b/esphome/components/pipsolar/sensor/__init__.py @@ -4,11 +4,18 @@ import esphome.config_validation as cv from esphome.const import ( CONF_BATTERY_VOLTAGE, CONF_BUS_VOLTAGE, + DEVICE_CLASS_APPARENT_POWER, + DEVICE_CLASS_BATTERY, DEVICE_CLASS_CURRENT, + DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_POWER, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, + ICON_BATTERY, ICON_CURRENT_AC, + ICON_FLASH, + ICON_GAUGE, + STATE_CLASS_MEASUREMENT, UNIT_AMPERE, UNIT_CELSIUS, UNIT_HERTZ, @@ -22,6 +29,10 @@ from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA DEPENDENCIES = ["uart"] +ICON_SOLAR_POWER = "mdi:solar-power" +ICON_SOLAR_PANEL = "mdi:solar-panel" +ICON_CURRENT_DC = "mdi:current-dc" + # QPIRI sensors CONF_GRID_RATING_VOLTAGE = "grid_rating_voltage" CONF_GRID_RATING_CURRENT = "grid_rating_current" @@ -75,16 +86,19 @@ TYPES = { unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_GRID_RATING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, accuracy_decimals=1, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_RATING_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_RATING_FREQUENCY: sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, @@ -98,11 +112,12 @@ TYPES = { ), CONF_AC_OUTPUT_RATING_APPARENT_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS, - accuracy_decimals=1, + accuracy_decimals=0, + device_class=DEVICE_CLASS_APPARENT_POWER, ), CONF_AC_OUTPUT_RATING_ACTIVE_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_POWER, ), CONF_BATTERY_RATING_VOLTAGE: sensor.sensor_schema( @@ -131,124 +146,151 @@ TYPES = { device_class=DEVICE_CLASS_VOLTAGE, ), CONF_BATTERY_TYPE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_CURRENT_MAX_AC_CHARGING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, ), CONF_CURRENT_MAX_CHARGING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, ), CONF_INPUT_VOLTAGE_RANGE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_OUTPUT_SOURCE_PRIORITY: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_CHARGER_SOURCE_PRIORITY: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_PARALLEL_MAX_NUM: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_MACHINE_TYPE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_TOPOLOGY: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_OUTPUT_MODE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_BATTERY_REDISCHARGE_VOLTAGE: sensor.sensor_schema( accuracy_decimals=1, ), CONF_PV_OK_CONDITION_FOR_PARALLEL: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_PV_POWER_BALANCE: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_GRID_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_GRID_FREQUENCY: sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, icon=ICON_CURRENT_AC, accuracy_decimals=1, + device_class=DEVICE_CLASS_FREQUENCY, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_FREQUENCY: sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, icon=ICON_CURRENT_AC, accuracy_decimals=1, + device_class=DEVICE_CLASS_FREQUENCY, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_APPARENT_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT_AMPS, - accuracy_decimals=1, + accuracy_decimals=0, + device_class=DEVICE_CLASS_APPARENT_POWER, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_AC_OUTPUT_ACTIVE_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_OUTPUT_LOAD_PERCENT: sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, - accuracy_decimals=1, + icon=ICON_GAUGE, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BUS_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, + icon=ICON_FLASH, + accuracy_decimals=0, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, + icon=ICON_BATTERY, + accuracy_decimals=2, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_CHARGING_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + icon=ICON_CURRENT_DC, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_CAPACITY_PERCENT: sensor.sensor_schema( unit_of_measurement=UNIT_PERCENT, - accuracy_decimals=1, + accuracy_decimals=0, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_INVERTER_HEAT_SINK_TEMPERATURE: sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=1, + accuracy_decimals=0, device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_PV_INPUT_CURRENT_FOR_BATTERY: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, + icon=ICON_SOLAR_PANEL, accuracy_decimals=1, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_PV_INPUT_VOLTAGE: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, + icon=ICON_SOLAR_PANEL, accuracy_decimals=1, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_VOLTAGE_SCC: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, - accuracy_decimals=1, + accuracy_decimals=2, device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_DISCHARGE_CURRENT: sensor.sensor_schema( unit_of_measurement=UNIT_AMPERE, - accuracy_decimals=1, + icon=ICON_CURRENT_DC, + accuracy_decimals=0, device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, ), CONF_BATTERY_VOLTAGE_OFFSET_FOR_FANS_ON: sensor.sensor_schema( unit_of_measurement=UNIT_VOLT, @@ -256,12 +298,14 @@ TYPES = { device_class=DEVICE_CLASS_VOLTAGE, ), CONF_EEPROM_VERSION: sensor.sensor_schema( - accuracy_decimals=1, + accuracy_decimals=0, ), CONF_PV_CHARGING_POWER: sensor.sensor_schema( unit_of_measurement=UNIT_WATT, - accuracy_decimals=1, + icon=ICON_SOLAR_POWER, + accuracy_decimals=0, device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, ), } diff --git a/requirements.txt b/requirements.txt index 351143591a..4a64bd39cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ esphome-dashboard==20251013.0 aioesphomeapi==42.3.0 zeroconf==0.148.0 puremagic==1.30 -ruamel.yaml==0.18.15 # dashboard_import +ruamel.yaml==0.18.16 # dashboard_import ruamel.yaml.clib==0.2.14 # dashboard_import esphome-glyphsets==0.2.0 pillow==11.3.0