mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Feature pipsolar anh (#1664)
Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com> Co-authored-by: Otto Winter <otto@otto-winter.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -87,6 +87,7 @@ esphome/components/number/* @esphome/core | ||||
| esphome/components/ota/* @esphome/core | ||||
| esphome/components/output/* @esphome/core | ||||
| esphome/components/pid/* @OttoWinter | ||||
| esphome/components/pipsolar/* @andreashergert1984 | ||||
| esphome/components/pmsa003i/* @sjtrny | ||||
| esphome/components/pn532/* @OttoWinter @jesserockz | ||||
| esphome/components/pn532_i2c/* @OttoWinter @jesserockz | ||||
|   | ||||
							
								
								
									
										32
									
								
								esphome/components/pipsolar/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								esphome/components/pipsolar/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import CONF_ID | ||||
| from esphome.components import uart | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
| CODEOWNERS = ["@andreashergert1984"] | ||||
| AUTO_LOAD = ["binary_sensor", "text_sensor", "sensor", "switch", "output"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| CONF_PIPSOLAR_ID = "pipsolar_id" | ||||
|  | ||||
| pipsolar_ns = cg.esphome_ns.namespace("pipsolar") | ||||
| PipsolarComponent = pipsolar_ns.class_("Pipsolar", cg.Component) | ||||
|  | ||||
| PIPSOLAR_COMPONENT_SCHEMA = cv.COMPONENT_SCHEMA.extend( | ||||
|     { | ||||
|         cv.Required(CONF_PIPSOLAR_ID): cv.use_id(PipsolarComponent), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema({cv.GenerateID(): cv.declare_id(PipsolarComponent)}) | ||||
|     .extend(cv.polling_component_schema("1s")) | ||||
|     .extend(uart.UART_DEVICE_SCHEMA) | ||||
| ) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield cg.register_component(var, config) | ||||
|     yield uart.register_uart_device(var, config) | ||||
							
								
								
									
										144
									
								
								esphome/components/pipsolar/binary_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								esphome/components/pipsolar/binary_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import binary_sensor | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
| ) | ||||
| from .. import PIPSOLAR_COMPONENT_SCHEMA, CONF_PIPSOLAR_ID | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
|  | ||||
| CONF_ADD_SBU_PRIORITY_VERSION = "add_sbu_priority_version" | ||||
| CONF_CONFIGURATION_STATUS = "configuration_status" | ||||
| CONF_SCC_FIRMWARE_VERSION = "scc_firmware_version" | ||||
| CONF_LOAD_STATUS = "load_status" | ||||
| CONF_BATTERY_VOLTAGE_TO_STEADY_WHILE_CHARGING = ( | ||||
|     "battery_voltage_to_steady_while_charging" | ||||
| ) | ||||
| CONF_CHARGING_STATUS = "charging_status" | ||||
| CONF_SCC_CHARGING_STATUS = "scc_charging_status" | ||||
| CONF_AC_CHARGING_STATUS = "ac_charging_status" | ||||
| CONF_CHARGING_TO_FLOATING_MODE = "charging_to_floating_mode" | ||||
| CONF_SWITCH_ON = "switch_on" | ||||
| CONF_DUSTPROOF_INSTALLED = "dustproof_installed" | ||||
| CONF_SILENCE_BUZZER_OPEN_BUZZER = "silence_buzzer_open_buzzer" | ||||
| CONF_OVERLOAD_BYPASS_FUNCTION = "overload_bypass_function" | ||||
| CONF_LCD_ESCAPE_TO_DEFAULT = "lcd_escape_to_default" | ||||
| CONF_OVERLOAD_RESTART_FUNCTION = "overload_restart_function" | ||||
| CONF_OVER_TEMPERATURE_RESTART_FUNCTION = "over_temperature_restart_function" | ||||
| CONF_BACKLIGHT_ON = "backlight_on" | ||||
| CONF_ALARM_ON_WHEN_PRIMARY_SOURCE_INTERRUPT = "alarm_on_when_primary_source_interrupt" | ||||
| CONF_FAULT_CODE_RECORD = "fault_code_record" | ||||
| CONF_POWER_SAVING = "power_saving" | ||||
|  | ||||
| CONF_WARNINGS_PRESENT = "warnings_present" | ||||
| CONF_FAULTS_PRESENT = "faults_present" | ||||
| CONF_WARNING_POWER_LOSS = "warning_power_loss" | ||||
| CONF_FAULT_INVERTER_FAULT = "fault_inverter_fault" | ||||
| CONF_FAULT_BUS_OVER = "fault_bus_over" | ||||
| CONF_FAULT_BUS_UNDER = "fault_bus_under" | ||||
| CONF_FAULT_BUS_SOFT_FAIL = "fault_bus_soft_fail" | ||||
| CONF_WARNING_LINE_FAIL = "warning_line_fail" | ||||
| CONF_FAULT_OPVSHORT = "fault_opvshort" | ||||
| CONF_FAULT_INVERTER_VOLTAGE_TOO_LOW = "fault_inverter_voltage_too_low" | ||||
| CONF_FAULT_INVERTER_VOLTAGE_TOO_HIGH = "fault_inverter_voltage_too_high" | ||||
| CONF_WARNING_OVER_TEMPERATURE = "warning_over_temperature" | ||||
| CONF_WARNING_FAN_LOCK = "warning_fan_lock" | ||||
| CONF_WARNING_BATTERY_VOLTAGE_HIGH = "warning_battery_voltage_high" | ||||
| CONF_WARNING_BATTERY_LOW_ALARM = "warning_battery_low_alarm" | ||||
| CONF_WARNING_BATTERY_UNDER_SHUTDOWN = "warning_battery_under_shutdown" | ||||
| CONF_WARNING_BATTERY_DERATING = "warning_battery_derating" | ||||
| CONF_WARNING_OVER_LOAD = "warning_over_load" | ||||
| CONF_WARNING_EEPROM_FAILED = "warning_eeprom_failed" | ||||
| CONF_FAULT_INVERTER_OVER_CURRENT = "fault_inverter_over_current" | ||||
| CONF_FAULT_INVERTER_SOFT_FAILED = "fault_inverter_soft_failed" | ||||
| CONF_FAULT_SELF_TEST_FAILED = "fault_self_test_failed" | ||||
| CONF_FAULT_OP_DC_VOLTAGE_OVER = "fault_op_dc_voltage_over" | ||||
| CONF_FAULT_BATTERY_OPEN = "fault_battery_open" | ||||
| CONF_FAULT_CURRENT_SENSOR_FAILED = "fault_current_sensor_failed" | ||||
| CONF_FAULT_BATTERY_SHORT = "fault_battery_short" | ||||
| CONF_WARNING_POWER_LIMIT = "warning_power_limit" | ||||
| CONF_WARNING_PV_VOLTAGE_HIGH = "warning_pv_voltage_high" | ||||
| CONF_FAULT_MPPT_OVERLOAD = "fault_mppt_overload" | ||||
| CONF_WARNING_MPPT_OVERLOAD = "warning_mppt_overload" | ||||
| CONF_WARNING_BATTERY_TOO_LOW_TO_CHARGE = "warning_battery_too_low_to_charge" | ||||
| CONF_FAULT_DC_DC_OVER_CURRENT = "fault_dc_dc_over_current" | ||||
| CONF_FAULT_CODE = "fault_code" | ||||
| CONF_WARNUNG_LOW_PV_ENERGY = "warnung_low_pv_energy" | ||||
| CONF_WARNING_HIGH_AC_INPUT_DURING_BUS_SOFT_START = ( | ||||
|     "warning_high_ac_input_during_bus_soft_start" | ||||
| ) | ||||
| CONF_WARNING_BATTERY_EQUALIZATION = "warning_battery_equalization" | ||||
|  | ||||
| TYPES = [ | ||||
|     CONF_ADD_SBU_PRIORITY_VERSION, | ||||
|     CONF_CONFIGURATION_STATUS, | ||||
|     CONF_SCC_FIRMWARE_VERSION, | ||||
|     CONF_LOAD_STATUS, | ||||
|     CONF_BATTERY_VOLTAGE_TO_STEADY_WHILE_CHARGING, | ||||
|     CONF_CHARGING_STATUS, | ||||
|     CONF_SCC_CHARGING_STATUS, | ||||
|     CONF_AC_CHARGING_STATUS, | ||||
|     CONF_CHARGING_TO_FLOATING_MODE, | ||||
|     CONF_SWITCH_ON, | ||||
|     CONF_DUSTPROOF_INSTALLED, | ||||
|     CONF_SILENCE_BUZZER_OPEN_BUZZER, | ||||
|     CONF_OVERLOAD_BYPASS_FUNCTION, | ||||
|     CONF_LCD_ESCAPE_TO_DEFAULT, | ||||
|     CONF_OVERLOAD_RESTART_FUNCTION, | ||||
|     CONF_OVER_TEMPERATURE_RESTART_FUNCTION, | ||||
|     CONF_BACKLIGHT_ON, | ||||
|     CONF_ALARM_ON_WHEN_PRIMARY_SOURCE_INTERRUPT, | ||||
|     CONF_FAULT_CODE_RECORD, | ||||
|     CONF_POWER_SAVING, | ||||
|     CONF_WARNINGS_PRESENT, | ||||
|     CONF_FAULTS_PRESENT, | ||||
|     CONF_WARNING_POWER_LOSS, | ||||
|     CONF_FAULT_INVERTER_FAULT, | ||||
|     CONF_FAULT_BUS_OVER, | ||||
|     CONF_FAULT_BUS_UNDER, | ||||
|     CONF_FAULT_BUS_SOFT_FAIL, | ||||
|     CONF_WARNING_LINE_FAIL, | ||||
|     CONF_FAULT_OPVSHORT, | ||||
|     CONF_FAULT_INVERTER_VOLTAGE_TOO_LOW, | ||||
|     CONF_FAULT_INVERTER_VOLTAGE_TOO_HIGH, | ||||
|     CONF_WARNING_OVER_TEMPERATURE, | ||||
|     CONF_WARNING_FAN_LOCK, | ||||
|     CONF_WARNING_BATTERY_VOLTAGE_HIGH, | ||||
|     CONF_WARNING_BATTERY_LOW_ALARM, | ||||
|     CONF_WARNING_BATTERY_UNDER_SHUTDOWN, | ||||
|     CONF_WARNING_BATTERY_DERATING, | ||||
|     CONF_WARNING_OVER_LOAD, | ||||
|     CONF_WARNING_EEPROM_FAILED, | ||||
|     CONF_FAULT_INVERTER_OVER_CURRENT, | ||||
|     CONF_FAULT_INVERTER_SOFT_FAILED, | ||||
|     CONF_FAULT_SELF_TEST_FAILED, | ||||
|     CONF_FAULT_OP_DC_VOLTAGE_OVER, | ||||
|     CONF_FAULT_BATTERY_OPEN, | ||||
|     CONF_FAULT_CURRENT_SENSOR_FAILED, | ||||
|     CONF_FAULT_BATTERY_SHORT, | ||||
|     CONF_WARNING_POWER_LIMIT, | ||||
|     CONF_WARNING_PV_VOLTAGE_HIGH, | ||||
|     CONF_FAULT_MPPT_OVERLOAD, | ||||
|     CONF_WARNING_MPPT_OVERLOAD, | ||||
|     CONF_WARNING_BATTERY_TOO_LOW_TO_CHARGE, | ||||
|     CONF_FAULT_DC_DC_OVER_CURRENT, | ||||
|     CONF_FAULT_CODE, | ||||
|     CONF_WARNUNG_LOW_PV_ENERGY, | ||||
|     CONF_WARNING_HIGH_AC_INPUT_DURING_BUS_SOFT_START, | ||||
|     CONF_WARNING_BATTERY_EQUALIZATION, | ||||
| ] | ||||
|  | ||||
| CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend( | ||||
|     {cv.Optional(type): binary_sensor.BINARY_SENSOR_SCHEMA for type in TYPES} | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     paren = await cg.get_variable(config[CONF_PIPSOLAR_ID]) | ||||
|     for type in TYPES: | ||||
|         if type in config: | ||||
|             conf = config[type] | ||||
|             sens = cg.new_Pvariable(conf[CONF_ID]) | ||||
|             await binary_sensor.register_binary_sensor(sens, conf) | ||||
|             cg.add(getattr(paren, f"set_{type}")(sens)) | ||||
							
								
								
									
										106
									
								
								esphome/components/pipsolar/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								esphome/components/pipsolar/output/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import automation | ||||
| from esphome.components import output | ||||
| from esphome.const import CONF_ID, CONF_VALUE | ||||
| from .. import PIPSOLAR_COMPONENT_SCHEMA, CONF_PIPSOLAR_ID, pipsolar_ns | ||||
|  | ||||
| DEPENDENCIES = ["pipsolar"] | ||||
|  | ||||
| PipsolarOutput = pipsolar_ns.class_("PipsolarOutput", output.FloatOutput) | ||||
| SetOutputAction = pipsolar_ns.class_("SetOutputAction", automation.Action) | ||||
|  | ||||
| CONF_POSSIBLE_VALUES = "possible_values" | ||||
|  | ||||
| # 3.11 PCVV<nn.n><cr>: Setting battery C.V. (constant voltage) charging voltage 48.0V ~ 58.4V for 48V unit | ||||
| # battery_bulk_voltage; | ||||
| # battery_recharge_voltage;     12V unit: 11V/11.3V/11.5V/11.8V/12V/12.3V/12.5V/12.8V | ||||
| #                               24V unit: 22V/22.5V/23V/23.5V/24V/24.5V/25V/25.5V | ||||
| #                               48V unit: 44V/45V/46V/47V/48V/49V/50V/51V | ||||
| # battery_under_voltage;        40.0V ~ 48.0V for 48V unit | ||||
| # battery_float_voltage;        48.0V ~ 58.4V for 48V unit | ||||
| # battery_type;  00 for AGM, 01 for Flooded battery | ||||
| # current_max_ac_charging_current; | ||||
| # output_source_priority; 00 / 01 / 02 | ||||
| # charger_source_priority;  For HS: 00 for utility first, 01 for solar first, 02 for solar and utility, 03 for only solar charging | ||||
| #                           For MS/MSX: 00 for utility first, 01 for solar first, 03 for only solar charging | ||||
| # battery_redischarge_voltage;  12V unit: 00.0V12V/12.3V/12.5V/12.8V/13V/13.3V/13.5V/13.8V/14V/14.3V/14.5 | ||||
| #                               24V unit: 00.0V/24V/24.5V/25V/25.5V/26V/26.5V/27V/27.5V/28V/28.5V/29V | ||||
| #                               48V unit: 00.0V48V/49V/50V/51V/52V/53V/54V/55V/56V/57V/58V | ||||
|  | ||||
| CONF_BATTERY_RECHARGE_VOLTAGE = "battery_recharge_voltage" | ||||
| CONF_BATTERY_UNDER_VOLTAGE = "battery_under_voltage" | ||||
| CONF_BATTERY_FLOAT_VOLTAGE = "battery_float_voltage" | ||||
| CONF_BATTERY_TYPE = "battery_type" | ||||
| CONF_CURRENT_MAX_AC_CHARGING_CURRENT = "current_max_ac_charging_current" | ||||
| CONF_CURRENT_MAX_CHARGING_CURRENT = "current_max_charging_current" | ||||
| CONF_OUTPUT_SOURCE_PRIORITY = "output_source_priority" | ||||
| CONF_CHARGER_SOURCE_PRIORITY = "charger_source_priority" | ||||
| CONF_BATTERY_REDISCHARGE_VOLTAGE = "battery_redischarge_voltage" | ||||
|  | ||||
| TYPES = { | ||||
|     CONF_BATTERY_RECHARGE_VOLTAGE: ( | ||||
|         [44.0, 45.0, 46.0, 47.0, 48.0, 49.0, 50.0, 51.0], | ||||
|         "PBCV%02.1f", | ||||
|     ), | ||||
|     CONF_BATTERY_UNDER_VOLTAGE: ( | ||||
|         [40.0, 40.1, 42, 43, 44, 45, 46, 47, 48.0], | ||||
|         "PSDV%02.1f", | ||||
|     ), | ||||
|     CONF_BATTERY_FLOAT_VOLTAGE: ([48.0, 49.0, 50.0, 51.0], "PBFT%02.1f"), | ||||
|     CONF_BATTERY_TYPE: ([0, 1, 2], "PBT%02.0f"), | ||||
|     CONF_CURRENT_MAX_AC_CHARGING_CURRENT: ([2, 10, 20], "MUCHGC0%02.0f"), | ||||
|     CONF_CURRENT_MAX_CHARGING_CURRENT: ([10, 20, 30, 40], "MCHGC0%02.0f"), | ||||
|     CONF_OUTPUT_SOURCE_PRIORITY: ([0, 1, 2], "POP%02.0f"), | ||||
|     CONF_CHARGER_SOURCE_PRIORITY: ([0, 1, 2, 3], "PCP%02.0f"), | ||||
|     CONF_BATTERY_REDISCHARGE_VOLTAGE: ( | ||||
|         [0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58], | ||||
|         "PBDV%02.1f", | ||||
|     ), | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend( | ||||
|     { | ||||
|         cv.Optional(type): output.FLOAT_OUTPUT_SCHEMA.extend( | ||||
|             { | ||||
|                 cv.Required(CONF_ID): cv.declare_id(PipsolarOutput), | ||||
|                 cv.Optional(CONF_POSSIBLE_VALUES, default=values): cv.All( | ||||
|                     cv.ensure_list(cv.positive_float), cv.Length(min=1) | ||||
|                 ), | ||||
|             } | ||||
|         ) | ||||
|         for type, (values, _) in TYPES.items() | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     paren = await cg.get_variable(config[CONF_PIPSOLAR_ID]) | ||||
|  | ||||
|     for type, (_, command) in TYPES.items(): | ||||
|         if type in config: | ||||
|             conf = config[type] | ||||
|             var = cg.new_Pvariable(conf[CONF_ID]) | ||||
|             await output.register_output(var, conf) | ||||
|             cg.add(var.set_parent(paren)) | ||||
|             cg.add(var.set_set_command(command)) | ||||
|             if (CONF_POSSIBLE_VALUES) in conf: | ||||
|                 cg.add(var.set_possible_values(conf[CONF_POSSIBLE_VALUES])) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "output.pipsolar.set_level", | ||||
|     SetOutputAction, | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_ID): cv.use_id(CONF_ID), | ||||
|             cv.Required(CONF_VALUE): cv.templatable(cv.positive_float), | ||||
|         } | ||||
|     ), | ||||
| ) | ||||
| def output_pipsolar_set_level_to_code(config, action_id, template_arg, args): | ||||
|     paren = yield cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, paren) | ||||
|     template_ = yield cg.templatable(config[CONF_VALUE], args, float) | ||||
|     cg.add(var.set_level(template_)) | ||||
|     yield var | ||||
							
								
								
									
										22
									
								
								esphome/components/pipsolar/output/pipsolar_output.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/pipsolar/output/pipsolar_output.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| #include "pipsolar_output.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
|  | ||||
| static const char *const TAG = "pipsolar.output"; | ||||
|  | ||||
| void PipsolarOutput::write_state(float state) { | ||||
|   char tmp[10]; | ||||
|   sprintf(tmp, this->set_command_.c_str(), state); | ||||
|  | ||||
|   if (std::find(this->possible_values_.begin(), this->possible_values_.end(), state) != this->possible_values_.end()) { | ||||
|     ESP_LOGD(TAG, "Will write: %s out of value %f / %02.0f", tmp, state, state); | ||||
|     this->parent_->switch_command(std::string(tmp)); | ||||
|   } else { | ||||
|     ESP_LOGD(TAG, "Will not write: %s as it is not in list of allowed values", tmp); | ||||
|   } | ||||
| } | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
							
								
								
									
										40
									
								
								esphome/components/pipsolar/output/pipsolar_output.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								esphome/components/pipsolar/output/pipsolar_output.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../pipsolar.h" | ||||
| #include "esphome/components/output/float_output.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
|  | ||||
| class Pipsolar; | ||||
|  | ||||
| class PipsolarOutput : public output::FloatOutput { | ||||
|  public: | ||||
|   PipsolarOutput() {} | ||||
|   void set_parent(Pipsolar *parent) { this->parent_ = parent; } | ||||
|   void set_set_command(std::string command) { this->set_command_ = std::move(command); }; | ||||
|   void set_possible_values(std::vector<float> possible_values) { this->possible_values_ = std::move(possible_values); } | ||||
|   void set_value(float value) { this->write_state(value); }; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(float state) override; | ||||
|   std::string set_command_; | ||||
|   Pipsolar *parent_; | ||||
|   std::vector<float> possible_values_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SetOutputAction : public Action<Ts...> { | ||||
|  public: | ||||
|   SetOutputAction(PipsolarOutput *output) : output_(output) {} | ||||
|  | ||||
|   TEMPLATABLE_VALUE(float, level) | ||||
|  | ||||
|   void play(Ts... x) override { this->output_->set_value(this->level_.value(x...)); } | ||||
|  | ||||
|  protected: | ||||
|   PipsolarOutput *output_; | ||||
| }; | ||||
|  | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
							
								
								
									
										922
									
								
								esphome/components/pipsolar/pipsolar.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										922
									
								
								esphome/components/pipsolar/pipsolar.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,922 @@ | ||||
| #include "pipsolar.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
|  | ||||
| static const char *const TAG = "pipsolar"; | ||||
|  | ||||
| void Pipsolar::setup() { | ||||
|   this->state_ = STATE_IDLE; | ||||
|   this->command_start_millis_ = 0; | ||||
| } | ||||
|  | ||||
| void Pipsolar::empty_uart_buffer_() { | ||||
|   uint8_t byte; | ||||
|   while (this->available()) { | ||||
|     this->read_byte(&byte); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void Pipsolar::loop() { | ||||
|   // Read message | ||||
|   if (this->state_ == STATE_IDLE) { | ||||
|     this->empty_uart_buffer_(); | ||||
|     switch (this->send_next_command_()) { | ||||
|       case 0: | ||||
|         // no command send (empty queue) time to poll | ||||
|         if (millis() - this->last_poll_ > this->update_interval_) { | ||||
|           this->send_next_poll_(); | ||||
|           this->last_poll_ = millis(); | ||||
|         } | ||||
|         return; | ||||
|         break; | ||||
|       case 1: | ||||
|         // command send | ||||
|         return; | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|   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') { | ||||
|           ESP_LOGD(TAG, "command successful"); | ||||
|         } else { | ||||
|           ESP_LOGD(TAG, "command not successful"); | ||||
|         } | ||||
|         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 | ||||
|         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", | ||||
|                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; | ||||
|       this->state_ = STATE_IDLE; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (this->state_ == STATE_POLL_DECODED) { | ||||
|     std::string mode; | ||||
|     switch (this->used_polling_commands_[this->last_polling_command_].identifier) { | ||||
|       case POLLING_QPIRI: | ||||
|         if (this->grid_rating_voltage_) { | ||||
|           this->grid_rating_voltage_->publish_state(value_grid_rating_voltage_); | ||||
|         } | ||||
|         if (this->grid_rating_current_) { | ||||
|           this->grid_rating_current_->publish_state(value_grid_rating_current_); | ||||
|         } | ||||
|         if (this->ac_output_rating_voltage_) { | ||||
|           this->ac_output_rating_voltage_->publish_state(value_ac_output_rating_voltage_); | ||||
|         } | ||||
|         if (this->ac_output_rating_frequency_) { | ||||
|           this->ac_output_rating_frequency_->publish_state(value_ac_output_rating_frequency_); | ||||
|         } | ||||
|         if (this->ac_output_rating_current_) { | ||||
|           this->ac_output_rating_current_->publish_state(value_ac_output_rating_current_); | ||||
|         } | ||||
|         if (this->ac_output_rating_apparent_power_) { | ||||
|           this->ac_output_rating_apparent_power_->publish_state(value_ac_output_rating_apparent_power_); | ||||
|         } | ||||
|         if (this->ac_output_rating_active_power_) { | ||||
|           this->ac_output_rating_active_power_->publish_state(value_ac_output_rating_active_power_); | ||||
|         } | ||||
|         if (this->battery_rating_voltage_) { | ||||
|           this->battery_rating_voltage_->publish_state(value_battery_rating_voltage_); | ||||
|         } | ||||
|         if (this->battery_recharge_voltage_) { | ||||
|           this->battery_recharge_voltage_->publish_state(value_battery_recharge_voltage_); | ||||
|         } | ||||
|         if (this->battery_under_voltage_) { | ||||
|           this->battery_under_voltage_->publish_state(value_battery_under_voltage_); | ||||
|         } | ||||
|         if (this->battery_bulk_voltage_) { | ||||
|           this->battery_bulk_voltage_->publish_state(value_battery_bulk_voltage_); | ||||
|         } | ||||
|         if (this->battery_float_voltage_) { | ||||
|           this->battery_float_voltage_->publish_state(value_battery_float_voltage_); | ||||
|         } | ||||
|         if (this->battery_type_) { | ||||
|           this->battery_type_->publish_state(value_battery_type_); | ||||
|         } | ||||
|         if (this->current_max_ac_charging_current_) { | ||||
|           this->current_max_ac_charging_current_->publish_state(value_current_max_ac_charging_current_); | ||||
|         } | ||||
|         if (this->current_max_charging_current_) { | ||||
|           this->current_max_charging_current_->publish_state(value_current_max_charging_current_); | ||||
|         } | ||||
|         if (this->input_voltage_range_) { | ||||
|           this->input_voltage_range_->publish_state(value_input_voltage_range_); | ||||
|         } | ||||
|         // special for input voltage range switch | ||||
|         if (this->input_voltage_range_switch_) { | ||||
|           this->input_voltage_range_switch_->publish_state(value_input_voltage_range_ == 1); | ||||
|         } | ||||
|         if (this->output_source_priority_) { | ||||
|           this->output_source_priority_->publish_state(value_output_source_priority_); | ||||
|         } | ||||
|         // special for output source priority switches | ||||
|         if (this->output_source_priority_utility_switch_) { | ||||
|           this->output_source_priority_utility_switch_->publish_state(value_output_source_priority_ == 0); | ||||
|         } | ||||
|         if (this->output_source_priority_solar_switch_) { | ||||
|           this->output_source_priority_solar_switch_->publish_state(value_output_source_priority_ == 1); | ||||
|         } | ||||
|         if (this->output_source_priority_battery_switch_) { | ||||
|           this->output_source_priority_battery_switch_->publish_state(value_output_source_priority_ == 2); | ||||
|         } | ||||
|         if (this->charger_source_priority_) { | ||||
|           this->charger_source_priority_->publish_state(value_charger_source_priority_); | ||||
|         } | ||||
|         if (this->parallel_max_num_) { | ||||
|           this->parallel_max_num_->publish_state(value_parallel_max_num_); | ||||
|         } | ||||
|         if (this->machine_type_) { | ||||
|           this->machine_type_->publish_state(value_machine_type_); | ||||
|         } | ||||
|         if (this->topology_) { | ||||
|           this->topology_->publish_state(value_topology_); | ||||
|         } | ||||
|         if (this->output_mode_) { | ||||
|           this->output_mode_->publish_state(value_output_mode_); | ||||
|         } | ||||
|         if (this->battery_redischarge_voltage_) { | ||||
|           this->battery_redischarge_voltage_->publish_state(value_battery_redischarge_voltage_); | ||||
|         } | ||||
|         if (this->pv_ok_condition_for_parallel_) { | ||||
|           this->pv_ok_condition_for_parallel_->publish_state(value_pv_ok_condition_for_parallel_); | ||||
|         } | ||||
|         // special for pv ok condition switch | ||||
|         if (this->pv_ok_condition_for_parallel_switch_) { | ||||
|           this->pv_ok_condition_for_parallel_switch_->publish_state(value_pv_ok_condition_for_parallel_ == 1); | ||||
|         } | ||||
|         if (this->pv_power_balance_) { | ||||
|           this->pv_power_balance_->publish_state(value_pv_power_balance_ == 1); | ||||
|         } | ||||
|         // special for power balance switch | ||||
|         if (this->pv_power_balance_switch_) { | ||||
|           this->pv_power_balance_switch_->publish_state(value_pv_power_balance_ == 1); | ||||
|         } | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|       case POLLING_QPIGS: | ||||
|         if (this->grid_voltage_) { | ||||
|           this->grid_voltage_->publish_state(value_grid_voltage_); | ||||
|         } | ||||
|         if (this->grid_frequency_) { | ||||
|           this->grid_frequency_->publish_state(value_grid_frequency_); | ||||
|         } | ||||
|         if (this->ac_output_voltage_) { | ||||
|           this->ac_output_voltage_->publish_state(value_ac_output_voltage_); | ||||
|         } | ||||
|         if (this->ac_output_frequency_) { | ||||
|           this->ac_output_frequency_->publish_state(value_ac_output_frequency_); | ||||
|         } | ||||
|         if (this->ac_output_apparent_power_) { | ||||
|           this->ac_output_apparent_power_->publish_state(value_ac_output_apparent_power_); | ||||
|         } | ||||
|         if (this->ac_output_active_power_) { | ||||
|           this->ac_output_active_power_->publish_state(value_ac_output_active_power_); | ||||
|         } | ||||
|         if (this->output_load_percent_) { | ||||
|           this->output_load_percent_->publish_state(value_output_load_percent_); | ||||
|         } | ||||
|         if (this->bus_voltage_) { | ||||
|           this->bus_voltage_->publish_state(value_bus_voltage_); | ||||
|         } | ||||
|         if (this->battery_voltage_) { | ||||
|           this->battery_voltage_->publish_state(value_battery_voltage_); | ||||
|         } | ||||
|         if (this->battery_charging_current_) { | ||||
|           this->battery_charging_current_->publish_state(value_battery_charging_current_); | ||||
|         } | ||||
|         if (this->battery_capacity_percent_) { | ||||
|           this->battery_capacity_percent_->publish_state(value_battery_capacity_percent_); | ||||
|         } | ||||
|         if (this->inverter_heat_sink_temperature_) { | ||||
|           this->inverter_heat_sink_temperature_->publish_state(value_inverter_heat_sink_temperature_); | ||||
|         } | ||||
|         if (this->pv_input_current_for_battery_) { | ||||
|           this->pv_input_current_for_battery_->publish_state(value_pv_input_current_for_battery_); | ||||
|         } | ||||
|         if (this->pv_input_voltage_) { | ||||
|           this->pv_input_voltage_->publish_state(value_pv_input_voltage_); | ||||
|         } | ||||
|         if (this->battery_voltage_scc_) { | ||||
|           this->battery_voltage_scc_->publish_state(value_battery_voltage_scc_); | ||||
|         } | ||||
|         if (this->battery_discharge_current_) { | ||||
|           this->battery_discharge_current_->publish_state(value_battery_discharge_current_); | ||||
|         } | ||||
|         if (this->add_sbu_priority_version_) { | ||||
|           this->add_sbu_priority_version_->publish_state(value_add_sbu_priority_version_); | ||||
|         } | ||||
|         if (this->configuration_status_) { | ||||
|           this->configuration_status_->publish_state(value_configuration_status_); | ||||
|         } | ||||
|         if (this->scc_firmware_version_) { | ||||
|           this->scc_firmware_version_->publish_state(value_scc_firmware_version_); | ||||
|         } | ||||
|         if (this->load_status_) { | ||||
|           this->load_status_->publish_state(value_load_status_); | ||||
|         } | ||||
|         if (this->battery_voltage_to_steady_while_charging_) { | ||||
|           this->battery_voltage_to_steady_while_charging_->publish_state( | ||||
|               value_battery_voltage_to_steady_while_charging_); | ||||
|         } | ||||
|         if (this->charging_status_) { | ||||
|           this->charging_status_->publish_state(value_charging_status_); | ||||
|         } | ||||
|         if (this->scc_charging_status_) { | ||||
|           this->scc_charging_status_->publish_state(value_scc_charging_status_); | ||||
|         } | ||||
|         if (this->ac_charging_status_) { | ||||
|           this->ac_charging_status_->publish_state(value_ac_charging_status_); | ||||
|         } | ||||
|         if (this->battery_voltage_offset_for_fans_on_) { | ||||
|           this->battery_voltage_offset_for_fans_on_->publish_state(value_battery_voltage_offset_for_fans_on_ / 10.0f); | ||||
|         }  //.1 scale | ||||
|         if (this->eeprom_version_) { | ||||
|           this->eeprom_version_->publish_state(value_eeprom_version_); | ||||
|         } | ||||
|         if (this->pv_charging_power_) { | ||||
|           this->pv_charging_power_->publish_state(value_pv_charging_power_); | ||||
|         } | ||||
|         if (this->charging_to_floating_mode_) { | ||||
|           this->charging_to_floating_mode_->publish_state(value_charging_to_floating_mode_); | ||||
|         } | ||||
|         if (this->switch_on_) { | ||||
|           this->switch_on_->publish_state(value_switch_on_); | ||||
|         } | ||||
|         if (this->dustproof_installed_) { | ||||
|           this->dustproof_installed_->publish_state(value_dustproof_installed_); | ||||
|         } | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|       case POLLING_QMOD: | ||||
|         if (this->device_mode_) { | ||||
|           mode = value_device_mode_; | ||||
|           this->device_mode_->publish_state(mode); | ||||
|         } | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|       case POLLING_QFLAG: | ||||
|         if (this->silence_buzzer_open_buzzer_) { | ||||
|           this->silence_buzzer_open_buzzer_->publish_state(value_silence_buzzer_open_buzzer_); | ||||
|         } | ||||
|         if (this->overload_bypass_function_) { | ||||
|           this->overload_bypass_function_->publish_state(value_overload_bypass_function_); | ||||
|         } | ||||
|         if (this->lcd_escape_to_default_) { | ||||
|           this->lcd_escape_to_default_->publish_state(value_lcd_escape_to_default_); | ||||
|         } | ||||
|         if (this->overload_restart_function_) { | ||||
|           this->overload_restart_function_->publish_state(value_overload_restart_function_); | ||||
|         } | ||||
|         if (this->over_temperature_restart_function_) { | ||||
|           this->over_temperature_restart_function_->publish_state(value_over_temperature_restart_function_); | ||||
|         } | ||||
|         if (this->backlight_on_) { | ||||
|           this->backlight_on_->publish_state(value_backlight_on_); | ||||
|         } | ||||
|         if (this->alarm_on_when_primary_source_interrupt_) { | ||||
|           this->alarm_on_when_primary_source_interrupt_->publish_state(value_alarm_on_when_primary_source_interrupt_); | ||||
|         } | ||||
|         if (this->fault_code_record_) { | ||||
|           this->fault_code_record_->publish_state(value_fault_code_record_); | ||||
|         } | ||||
|         if (this->power_saving_) { | ||||
|           this->power_saving_->publish_state(value_power_saving_); | ||||
|         } | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|       case POLLING_QPIWS: | ||||
|         if (this->warnings_present_) { | ||||
|           this->warnings_present_->publish_state(value_warnings_present_); | ||||
|         } | ||||
|         if (this->faults_present_) { | ||||
|           this->faults_present_->publish_state(value_faults_present_); | ||||
|         } | ||||
|         if (this->warning_power_loss_) { | ||||
|           this->warning_power_loss_->publish_state(value_warning_power_loss_); | ||||
|         } | ||||
|         if (this->fault_inverter_fault_) { | ||||
|           this->fault_inverter_fault_->publish_state(value_fault_inverter_fault_); | ||||
|         } | ||||
|         if (this->fault_bus_over_) { | ||||
|           this->fault_bus_over_->publish_state(value_fault_bus_over_); | ||||
|         } | ||||
|         if (this->fault_bus_under_) { | ||||
|           this->fault_bus_under_->publish_state(value_fault_bus_under_); | ||||
|         } | ||||
|         if (this->fault_bus_soft_fail_) { | ||||
|           this->fault_bus_soft_fail_->publish_state(value_fault_bus_soft_fail_); | ||||
|         } | ||||
|         if (this->warning_line_fail_) { | ||||
|           this->warning_line_fail_->publish_state(value_warning_line_fail_); | ||||
|         } | ||||
|         if (this->fault_opvshort_) { | ||||
|           this->fault_opvshort_->publish_state(value_fault_opvshort_); | ||||
|         } | ||||
|         if (this->fault_inverter_voltage_too_low_) { | ||||
|           this->fault_inverter_voltage_too_low_->publish_state(value_fault_inverter_voltage_too_low_); | ||||
|         } | ||||
|         if (this->fault_inverter_voltage_too_high_) { | ||||
|           this->fault_inverter_voltage_too_high_->publish_state(value_fault_inverter_voltage_too_high_); | ||||
|         } | ||||
|         if (this->warning_over_temperature_) { | ||||
|           this->warning_over_temperature_->publish_state(value_warning_over_temperature_); | ||||
|         } | ||||
|         if (this->warning_fan_lock_) { | ||||
|           this->warning_fan_lock_->publish_state(value_warning_fan_lock_); | ||||
|         } | ||||
|         if (this->warning_battery_voltage_high_) { | ||||
|           this->warning_battery_voltage_high_->publish_state(value_warning_battery_voltage_high_); | ||||
|         } | ||||
|         if (this->warning_battery_low_alarm_) { | ||||
|           this->warning_battery_low_alarm_->publish_state(value_warning_battery_low_alarm_); | ||||
|         } | ||||
|         if (this->warning_battery_under_shutdown_) { | ||||
|           this->warning_battery_under_shutdown_->publish_state(value_warning_battery_under_shutdown_); | ||||
|         } | ||||
|         if (this->warning_battery_derating_) { | ||||
|           this->warning_battery_derating_->publish_state(value_warning_battery_derating_); | ||||
|         } | ||||
|         if (this->warning_over_load_) { | ||||
|           this->warning_over_load_->publish_state(value_warning_over_load_); | ||||
|         } | ||||
|         if (this->warning_eeprom_failed_) { | ||||
|           this->warning_eeprom_failed_->publish_state(value_warning_eeprom_failed_); | ||||
|         } | ||||
|         if (this->fault_inverter_over_current_) { | ||||
|           this->fault_inverter_over_current_->publish_state(value_fault_inverter_over_current_); | ||||
|         } | ||||
|         if (this->fault_inverter_soft_failed_) { | ||||
|           this->fault_inverter_soft_failed_->publish_state(value_fault_inverter_soft_failed_); | ||||
|         } | ||||
|         if (this->fault_self_test_failed_) { | ||||
|           this->fault_self_test_failed_->publish_state(value_fault_self_test_failed_); | ||||
|         } | ||||
|         if (this->fault_op_dc_voltage_over_) { | ||||
|           this->fault_op_dc_voltage_over_->publish_state(value_fault_op_dc_voltage_over_); | ||||
|         } | ||||
|         if (this->fault_battery_open_) { | ||||
|           this->fault_battery_open_->publish_state(value_fault_battery_open_); | ||||
|         } | ||||
|         if (this->fault_current_sensor_failed_) { | ||||
|           this->fault_current_sensor_failed_->publish_state(value_fault_current_sensor_failed_); | ||||
|         } | ||||
|         if (this->fault_battery_short_) { | ||||
|           this->fault_battery_short_->publish_state(value_fault_battery_short_); | ||||
|         } | ||||
|         if (this->warning_power_limit_) { | ||||
|           this->warning_power_limit_->publish_state(value_warning_power_limit_); | ||||
|         } | ||||
|         if (this->warning_pv_voltage_high_) { | ||||
|           this->warning_pv_voltage_high_->publish_state(value_warning_pv_voltage_high_); | ||||
|         } | ||||
|         if (this->fault_mppt_overload_) { | ||||
|           this->fault_mppt_overload_->publish_state(value_fault_mppt_overload_); | ||||
|         } | ||||
|         if (this->warning_mppt_overload_) { | ||||
|           this->warning_mppt_overload_->publish_state(value_warning_mppt_overload_); | ||||
|         } | ||||
|         if (this->warning_battery_too_low_to_charge_) { | ||||
|           this->warning_battery_too_low_to_charge_->publish_state(value_warning_battery_too_low_to_charge_); | ||||
|         } | ||||
|         if (this->fault_dc_dc_over_current_) { | ||||
|           this->fault_dc_dc_over_current_->publish_state(value_fault_dc_dc_over_current_); | ||||
|         } | ||||
|         if (this->fault_code_) { | ||||
|           this->fault_code_->publish_state(value_fault_code_); | ||||
|         } | ||||
|         if (this->warnung_low_pv_energy_) { | ||||
|           this->warnung_low_pv_energy_->publish_state(value_warnung_low_pv_energy_); | ||||
|         } | ||||
|         if (this->warning_high_ac_input_during_bus_soft_start_) { | ||||
|           this->warning_high_ac_input_during_bus_soft_start_->publish_state( | ||||
|               value_warning_high_ac_input_during_bus_soft_start_); | ||||
|         } | ||||
|         if (this->warning_battery_equalization_) { | ||||
|           this->warning_battery_equalization_->publish_state(value_warning_battery_equalization_); | ||||
|         } | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|       case POLLING_QT: | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|       case POLLING_QMN: | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (this->state_ == STATE_POLL_CHECKED) { | ||||
|     bool enabled = true; | ||||
|     std::string fc; | ||||
|     char tmp[PIPSOLAR_READ_BUFFER_LENGTH]; | ||||
|     sprintf(tmp, "%s", this->read_buffer_); | ||||
|     switch (this->used_polling_commands_[this->last_polling_command_].identifier) { | ||||
|       case POLLING_QPIRI: | ||||
|         ESP_LOGD(TAG, "Decode QPIRI"); | ||||
|         sscanf(tmp, "(%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d %d %d %d %d %f %d %d",          // NOLINT | ||||
|                &value_grid_rating_voltage_, &value_grid_rating_current_, &value_ac_output_rating_voltage_,  // NOLINT | ||||
|                &value_ac_output_rating_frequency_, &value_ac_output_rating_current_,                        // NOLINT | ||||
|                &value_ac_output_rating_apparent_power_, &value_ac_output_rating_active_power_,              // NOLINT | ||||
|                &value_battery_rating_voltage_, &value_battery_recharge_voltage_,                            // NOLINT | ||||
|                &value_battery_under_voltage_, &value_battery_bulk_voltage_, &value_battery_float_voltage_,  // NOLINT | ||||
|                &value_battery_type_, &value_current_max_ac_charging_current_,                               // NOLINT | ||||
|                &value_current_max_charging_current_, &value_input_voltage_range_,                           // NOLINT | ||||
|                &value_output_source_priority_, &value_charger_source_priority_, &value_parallel_max_num_,   // NOLINT | ||||
|                &value_machine_type_, &value_topology_, &value_output_mode_,                                 // NOLINT | ||||
|                &value_battery_redischarge_voltage_, &value_pv_ok_condition_for_parallel_,                   // NOLINT | ||||
|                &value_pv_power_balance_);                                                                   // NOLINT | ||||
|         if (this->last_qpiri_) { | ||||
|           this->last_qpiri_->publish_state(tmp); | ||||
|         } | ||||
|         this->state_ = STATE_POLL_DECODED; | ||||
|         break; | ||||
|       case POLLING_QPIGS: | ||||
|         ESP_LOGD(TAG, "Decode QPIGS"); | ||||
|         sscanf(                                                                                              // NOLINT | ||||
|             tmp,                                                                                             // NOLINT | ||||
|             "(%f %f %f %f %d %d %d %d %f %d %d %d %d %f %f %d %1d%1d%1d%1d%1d%1d%1d%1d %d %d %d %1d%1d%1d",  // NOLINT | ||||
|             &value_grid_voltage_, &value_grid_frequency_, &value_ac_output_voltage_,                         // NOLINT | ||||
|             &value_ac_output_frequency_,                                                                     // NOLINT | ||||
|             &value_ac_output_apparent_power_, &value_ac_output_active_power_, &value_output_load_percent_,   // NOLINT | ||||
|             &value_bus_voltage_, &value_battery_voltage_, &value_battery_charging_current_,                  // NOLINT | ||||
|             &value_battery_capacity_percent_, &value_inverter_heat_sink_temperature_,                        // NOLINT | ||||
|             &value_pv_input_current_for_battery_, &value_pv_input_voltage_, &value_battery_voltage_scc_,     // NOLINT | ||||
|             &value_battery_discharge_current_, &value_add_sbu_priority_version_,                             // NOLINT | ||||
|             &value_configuration_status_, &value_scc_firmware_version_, &value_load_status_,                 // NOLINT | ||||
|             &value_battery_voltage_to_steady_while_charging_, &value_charging_status_,                       // NOLINT | ||||
|             &value_scc_charging_status_, &value_ac_charging_status_,                                         // NOLINT | ||||
|             &value_battery_voltage_offset_for_fans_on_, &value_eeprom_version_, &value_pv_charging_power_,   // NOLINT | ||||
|             &value_charging_to_floating_mode_, &value_switch_on_,                                            // NOLINT | ||||
|             &value_dustproof_installed_);                                                                    // NOLINT | ||||
|         if (this->last_qpigs_) { | ||||
|           this->last_qpigs_->publish_state(tmp); | ||||
|         } | ||||
|         this->state_ = STATE_POLL_DECODED; | ||||
|         break; | ||||
|       case POLLING_QMOD: | ||||
|         ESP_LOGD(TAG, "Decode QMOD"); | ||||
|         this->value_device_mode_ = char(this->read_buffer_[1]); | ||||
|         if (this->last_qmod_) { | ||||
|           this->last_qmod_->publish_state(tmp); | ||||
|         } | ||||
|         this->state_ = STATE_POLL_DECODED; | ||||
|         break; | ||||
|       case POLLING_QFLAG: | ||||
|         ESP_LOGD(TAG, "Decode QFLAG"); | ||||
|         // result like:"(EbkuvxzDajy" | ||||
|         // get through all char: ignore first "(" Enable flag on 'E', Disable on 'D') else set the corresponding value | ||||
|         for (int i = 1; i < strlen(tmp); i++) { | ||||
|           switch (tmp[i]) { | ||||
|             case 'E': | ||||
|               enabled = true; | ||||
|               break; | ||||
|             case 'D': | ||||
|               enabled = false; | ||||
|               break; | ||||
|             case 'a': | ||||
|               this->value_silence_buzzer_open_buzzer_ = enabled; | ||||
|               break; | ||||
|             case 'b': | ||||
|               this->value_overload_bypass_function_ = enabled; | ||||
|               break; | ||||
|             case 'k': | ||||
|               this->value_lcd_escape_to_default_ = enabled; | ||||
|               break; | ||||
|             case 'u': | ||||
|               this->value_overload_restart_function_ = enabled; | ||||
|               break; | ||||
|             case 'v': | ||||
|               this->value_over_temperature_restart_function_ = enabled; | ||||
|               break; | ||||
|             case 'x': | ||||
|               this->value_backlight_on_ = enabled; | ||||
|               break; | ||||
|             case 'y': | ||||
|               this->value_alarm_on_when_primary_source_interrupt_ = enabled; | ||||
|               break; | ||||
|             case 'z': | ||||
|               this->value_fault_code_record_ = enabled; | ||||
|               break; | ||||
|             case 'j': | ||||
|               this->value_power_saving_ = enabled; | ||||
|               break; | ||||
|           } | ||||
|         } | ||||
|         if (this->last_qflag_) { | ||||
|           this->last_qflag_->publish_state(tmp); | ||||
|         } | ||||
|         this->state_ = STATE_POLL_DECODED; | ||||
|         break; | ||||
|       case POLLING_QPIWS: | ||||
|         ESP_LOGD(TAG, "Decode QPIWS"); | ||||
|         // '(00000000000000000000000000000000' | ||||
|         // iterate over all available flag (as not all models have all flags, but at least in the same order) | ||||
|         this->value_warnings_present_ = false; | ||||
|         this->value_faults_present_ = true; | ||||
|  | ||||
|         for (int i = 1; i < strlen(tmp); i++) { | ||||
|           enabled = tmp[i] == '1'; | ||||
|           switch (i) { | ||||
|             case 1: | ||||
|               this->value_warning_power_loss_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 2: | ||||
|               this->value_fault_inverter_fault_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 3: | ||||
|               this->value_fault_bus_over_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 4: | ||||
|               this->value_fault_bus_under_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 5: | ||||
|               this->value_fault_bus_soft_fail_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 6: | ||||
|               this->value_warning_line_fail_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 7: | ||||
|               this->value_fault_opvshort_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 8: | ||||
|               this->value_fault_inverter_voltage_too_low_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 9: | ||||
|               this->value_fault_inverter_voltage_too_high_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 10: | ||||
|               this->value_warning_over_temperature_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 11: | ||||
|               this->value_warning_fan_lock_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 12: | ||||
|               this->value_warning_battery_voltage_high_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 13: | ||||
|               this->value_warning_battery_low_alarm_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 15: | ||||
|               this->value_warning_battery_under_shutdown_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 16: | ||||
|               this->value_warning_battery_derating_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 17: | ||||
|               this->value_warning_over_load_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 18: | ||||
|               this->value_warning_eeprom_failed_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 19: | ||||
|               this->value_fault_inverter_over_current_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 20: | ||||
|               this->value_fault_inverter_soft_failed_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 21: | ||||
|               this->value_fault_self_test_failed_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 22: | ||||
|               this->value_fault_op_dc_voltage_over_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 23: | ||||
|               this->value_fault_battery_open_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 24: | ||||
|               this->value_fault_current_sensor_failed_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 25: | ||||
|               this->value_fault_battery_short_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 26: | ||||
|               this->value_warning_power_limit_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 27: | ||||
|               this->value_warning_pv_voltage_high_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 28: | ||||
|               this->value_fault_mppt_overload_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 29: | ||||
|               this->value_warning_mppt_overload_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 30: | ||||
|               this->value_warning_battery_too_low_to_charge_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 31: | ||||
|               this->value_fault_dc_dc_over_current_ = enabled; | ||||
|               this->value_faults_present_ += enabled; | ||||
|               break; | ||||
|             case 32: | ||||
|               fc = tmp[i]; | ||||
|               fc += tmp[i + 1]; | ||||
|               this->value_fault_code_ = strtol(fc.c_str(), nullptr, 10); | ||||
|               break; | ||||
|             case 34: | ||||
|               this->value_warnung_low_pv_energy_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 35: | ||||
|               this->value_warning_high_ac_input_during_bus_soft_start_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|             case 36: | ||||
|               this->value_warning_battery_equalization_ = enabled; | ||||
|               this->value_warnings_present_ += enabled; | ||||
|               break; | ||||
|           } | ||||
|         } | ||||
|         if (this->last_qpiws_) { | ||||
|           this->last_qpiws_->publish_state(tmp); | ||||
|         } | ||||
|         this->state_ = STATE_POLL_DECODED; | ||||
|         break; | ||||
|       case POLLING_QT: | ||||
|         ESP_LOGD(TAG, "Decode QT"); | ||||
|         if (this->last_qt_) { | ||||
|           this->last_qt_->publish_state(tmp); | ||||
|         } | ||||
|         this->state_ = STATE_POLL_DECODED; | ||||
|         break; | ||||
|       case POLLING_QMN: | ||||
|         ESP_LOGD(TAG, "Decode QMN"); | ||||
|         if (this->last_qmn_) { | ||||
|           this->last_qmn_->publish_state(tmp); | ||||
|         } | ||||
|         this->state_ = STATE_POLL_DECODED; | ||||
|         break; | ||||
|       default: | ||||
|         this->state_ = STATE_IDLE; | ||||
|         break; | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (this->state_ == STATE_POLL_COMPLETE) { | ||||
|     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') { | ||||
|         this->state_ = STATE_IDLE; | ||||
|         return; | ||||
|       } | ||||
|       // crc ok | ||||
|       this->state_ = STATE_POLL_CHECKED; | ||||
|       return; | ||||
|     } else { | ||||
|       this->state_ = STATE_IDLE; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (this->state_ == STATE_COMMAND || this->state_ == STATE_POLL) { | ||||
|     while (this->available()) { | ||||
|       uint8_t byte; | ||||
|       this->read_byte(&byte); | ||||
|  | ||||
|       if (this->read_pos_ == PIPSOLAR_READ_BUFFER_LENGTH) { | ||||
|         this->read_pos_ = 0; | ||||
|         this->empty_uart_buffer_(); | ||||
|       } | ||||
|       this->read_buffer_[this->read_pos_] = byte; | ||||
|       this->read_pos_++; | ||||
|  | ||||
|       // end of answer | ||||
|       if (byte == 0x0D) { | ||||
|         this->read_buffer_[this->read_pos_] = 0; | ||||
|         this->empty_uart_buffer_(); | ||||
|         if (this->state_ == STATE_POLL) { | ||||
|           this->state_ = STATE_POLL_COMPLETE; | ||||
|         } | ||||
|         if (this->state_ == STATE_COMMAND) { | ||||
|           this->state_ = STATE_COMMAND_COMPLETE; | ||||
|         } | ||||
|       } | ||||
|     }  // available | ||||
|   } | ||||
|   if (this->state_ == STATE_COMMAND) { | ||||
|     if (millis() - this->command_start_millis_ > esphome::pipsolar::Pipsolar::COMMAND_TIMEOUT) { | ||||
|       // 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); | ||||
|       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->used_polling_commands_[this->last_polling_command_].command); | ||||
|       this->state_ = STATE_IDLE; | ||||
|     } else { | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| uint8_t Pipsolar::check_incoming_length_(uint8_t length) { | ||||
|   if (this->read_pos_ - 3 == length) { | ||||
|     return 1; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| uint8_t Pipsolar::check_incoming_crc_() { | ||||
|   uint16_t crc16; | ||||
|   crc16 = calc_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"); | ||||
|     read_buffer_[read_pos_ - 1] = 0; | ||||
|     read_buffer_[read_pos_ - 2] = 0; | ||||
|     read_buffer_[read_pos_ - 3] = 0; | ||||
|     return 1; | ||||
|   } | ||||
|   ESP_LOGD(TAG, "CRC NOK expected: %X %X but got: %X %X", ((uint8_t)((crc16) >> 8)), ((uint8_t)((crc16) &0xff)), | ||||
|            read_buffer_[read_pos_ - 3], read_buffer_[read_pos_ - 2]); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| // send next command used | ||||
| uint8_t Pipsolar::send_next_command_() { | ||||
|   uint16_t crc16; | ||||
|   if (this->command_queue_[this->command_queue_position_].length() != 0) { | ||||
|     const char *command = this->command_queue_[this->command_queue_position_].c_str(); | ||||
|     uint8_t byte_command[16]; | ||||
|     uint8_t length = this->command_queue_[this->command_queue_position_].length(); | ||||
|     for (uint8_t i = 0; i < length; i++) { | ||||
|       byte_command[i] = (uint8_t) this->command_queue_[this->command_queue_position_].at(i); | ||||
|     } | ||||
|     this->state_ = STATE_COMMAND; | ||||
|     this->command_start_millis_ = millis(); | ||||
|     this->empty_uart_buffer_(); | ||||
|     this->read_pos_ = 0; | ||||
|     crc16 = calc_crc_(byte_command, length); | ||||
|     this->write_str(command); | ||||
|     // checksum | ||||
|     this->write(((uint8_t)((crc16) >> 8)));   // highbyte | ||||
|     this->write(((uint8_t)((crc16) &0xff)));  // lowbyte | ||||
|     // end Byte | ||||
|     this->write(0x0D); | ||||
|     ESP_LOGD(TAG, "Sending command from queue: %s with length %d", command, length); | ||||
|     return 1; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| void Pipsolar::send_next_poll_() { | ||||
|   uint16_t crc16; | ||||
|   this->last_polling_command_ = (this->last_polling_command_ + 1) % 15; | ||||
|   if (this->used_polling_commands_[this->last_polling_command_].length == 0) { | ||||
|     this->last_polling_command_ = 0; | ||||
|   } | ||||
|   if (this->used_polling_commands_[this->last_polling_command_].length == 0) { | ||||
|     // no command specified | ||||
|     return; | ||||
|   } | ||||
|   this->state_ = STATE_POLL; | ||||
|   this->command_start_millis_ = millis(); | ||||
|   this->empty_uart_buffer_(); | ||||
|   this->read_pos_ = 0; | ||||
|   crc16 = calc_crc_(this->used_polling_commands_[this->last_polling_command_].command, | ||||
|                     this->used_polling_commands_[this->last_polling_command_].length); | ||||
|   this->write_array(this->used_polling_commands_[this->last_polling_command_].command, | ||||
|                     this->used_polling_commands_[this->last_polling_command_].length); | ||||
|   // checksum | ||||
|   this->write(((uint8_t)((crc16) >> 8)));   // highbyte | ||||
|   this->write(((uint8_t)((crc16) &0xff)));  // lowbyte | ||||
|   // end Byte | ||||
|   this->write(0x0D); | ||||
|   ESP_LOGD(TAG, "Sending polling command : %s with length %d", | ||||
|            this->used_polling_commands_[this->last_polling_command_].command, | ||||
|            this->used_polling_commands_[this->last_polling_command_].length); | ||||
| } | ||||
|  | ||||
| void Pipsolar::queue_command_(const char *command, byte length) { | ||||
|   uint8_t next_position = command_queue_position_; | ||||
|   for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) { | ||||
|     uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH; | ||||
|     if (command_queue_[testposition].length() == 0) { | ||||
|       command_queue_[testposition] = command; | ||||
|       ESP_LOGD(TAG, "Command queued successfully: %s with length %u at position %d", command, | ||||
|                command_queue_[testposition].length(), testposition); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGD(TAG, "Command queue full dropping command: %s", command); | ||||
| } | ||||
|  | ||||
| void Pipsolar::switch_command(const std::string &command) { | ||||
|   ESP_LOGD(TAG, "got command: %s", command.c_str()); | ||||
|   queue_command_(command.c_str(), command.length()); | ||||
| } | ||||
| void Pipsolar::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "Pipsolar:"); | ||||
|   ESP_LOGCONFIG(TAG, "used commands:"); | ||||
|   for (auto &used_polling_command : this->used_polling_commands_) { | ||||
|     if (used_polling_command.length != 0) { | ||||
|       ESP_LOGCONFIG(TAG, "%s", used_polling_command.command); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| void Pipsolar::update() {} | ||||
|  | ||||
| void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand polling_command) { | ||||
|   for (auto &used_polling_command : this->used_polling_commands_) { | ||||
|     if (used_polling_command.length == strlen(command)) { | ||||
|       uint8_t len = strlen(command); | ||||
|       if (memcmp(used_polling_command.command, command, len) == 0) { | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|     if (used_polling_command.length == 0) { | ||||
|       size_t length = strlen(command) + 1; | ||||
|       const char *beg = command; | ||||
|       const char *end = command + length; | ||||
|       used_polling_command.command = new uint8_t[length]; | ||||
|       size_t i = 0; | ||||
|       for (; beg != end; ++beg, ++i) { | ||||
|         used_polling_command.command[i] = (uint8_t)(*beg); | ||||
|       } | ||||
|       used_polling_command.errors = 0; | ||||
|       used_polling_command.identifier = polling_command; | ||||
|       used_polling_command.length = length - 1; | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| uint16_t Pipsolar::calc_crc_(uint8_t *msg, int n) { | ||||
|   // Initial value. xmodem uses 0xFFFF but this example | ||||
|   // requires an initial value of zero. | ||||
|   uint16_t x = 0; | ||||
|   while (n--) { | ||||
|     x = crc_xmodem_update_(x, (uint16_t) *msg++); | ||||
|   } | ||||
|   return (x); | ||||
| } | ||||
|  | ||||
| // See bottom of this page: http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html | ||||
| // Polynomial: x^16 + x^12 + x^5 + 1 (0x1021) | ||||
| uint16_t Pipsolar::crc_xmodem_update_(uint16_t crc, uint8_t data) { | ||||
|   int i; | ||||
|   crc = crc ^ ((uint16_t) data << 8); | ||||
|   for (i = 0; i < 8; i++) { | ||||
|     if (crc & 0x8000) | ||||
|       crc = (crc << 1) ^ 0x1021;  //(polynomial = 0x1021) | ||||
|     else | ||||
|       crc <<= 1; | ||||
|   } | ||||
|   return crc; | ||||
| } | ||||
|  | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
							
								
								
									
										223
									
								
								esphome/components/pipsolar/pipsolar.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								esphome/components/pipsolar/pipsolar.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/binary_sensor/binary_sensor.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
|  | ||||
| enum ENUMPollingCommand { | ||||
|   POLLING_QPIRI = 0, | ||||
|   POLLING_QPIGS = 1, | ||||
|   POLLING_QMOD = 2, | ||||
|   POLLING_QFLAG = 3, | ||||
|   POLLING_QPIWS = 4, | ||||
|   POLLING_QT = 5, | ||||
|   POLLING_QMN = 6, | ||||
| }; | ||||
| struct PollingCommand { | ||||
|   uint8_t *command; | ||||
|   uint8_t length = 0; | ||||
|   uint8_t errors; | ||||
|   ENUMPollingCommand identifier; | ||||
| }; | ||||
|  | ||||
| #define PIPSOLAR_VALUED_ENTITY_(type, name, polling_command, value_type) \ | ||||
|  protected: \ | ||||
|   value_type value_##name##_; \ | ||||
|   PIPSOLAR_ENTITY_(type, name, polling_command) | ||||
|  | ||||
| #define PIPSOLAR_ENTITY_(type, name, polling_command) \ | ||||
|  protected: \ | ||||
|   type *name##_{}; /* NOLINT */ \ | ||||
| \ | ||||
|  public: \ | ||||
|   void set_##name(type *name) { /* NOLINT */ \ | ||||
|     this->name##_ = name; \ | ||||
|     this->add_polling_command_(#polling_command, POLLING_##polling_command); \ | ||||
|   } | ||||
|  | ||||
| #define PIPSOLAR_SENSOR(name, polling_command, value_type) \ | ||||
|   PIPSOLAR_VALUED_ENTITY_(sensor::Sensor, name, polling_command, value_type) | ||||
| #define PIPSOLAR_SWITCH(name, polling_command) PIPSOLAR_ENTITY_(switch_::Switch, name, polling_command) | ||||
| #define PIPSOLAR_BINARY_SENSOR(name, polling_command, value_type) \ | ||||
|   PIPSOLAR_VALUED_ENTITY_(binary_sensor::BinarySensor, name, polling_command, value_type) | ||||
| #define PIPSOLAR_VALUED_TEXT_SENSOR(name, polling_command, value_type) \ | ||||
|   PIPSOLAR_VALUED_ENTITY_(text_sensor::TextSensor, name, polling_command, value_type) | ||||
| #define PIPSOLAR_TEXT_SENSOR(name, polling_command) PIPSOLAR_ENTITY_(text_sensor::TextSensor, name, polling_command) | ||||
|  | ||||
| class Pipsolar : public uart::UARTDevice, public PollingComponent { | ||||
|   // QPIGS values | ||||
|   PIPSOLAR_SENSOR(grid_voltage, QPIGS, float) | ||||
|   PIPSOLAR_SENSOR(grid_frequency, QPIGS, float) | ||||
|   PIPSOLAR_SENSOR(ac_output_voltage, QPIGS, float) | ||||
|   PIPSOLAR_SENSOR(ac_output_frequency, QPIGS, float) | ||||
|   PIPSOLAR_SENSOR(ac_output_apparent_power, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(ac_output_active_power, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(output_load_percent, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(bus_voltage, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(battery_voltage, QPIGS, float) | ||||
|   PIPSOLAR_SENSOR(battery_charging_current, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(battery_capacity_percent, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(inverter_heat_sink_temperature, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(pv_input_current_for_battery, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(pv_input_voltage, QPIGS, float) | ||||
|   PIPSOLAR_SENSOR(battery_voltage_scc, QPIGS, float) | ||||
|   PIPSOLAR_SENSOR(battery_discharge_current, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(add_sbu_priority_version, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(configuration_status, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(scc_firmware_version, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(load_status, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(battery_voltage_to_steady_while_charging, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(charging_status, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(scc_charging_status, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(ac_charging_status, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(battery_voltage_offset_for_fans_on, QPIGS, int)  //.1 scale | ||||
|   PIPSOLAR_SENSOR(eeprom_version, QPIGS, int) | ||||
|   PIPSOLAR_SENSOR(pv_charging_power, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(charging_to_floating_mode, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(switch_on, QPIGS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(dustproof_installed, QPIGS, int) | ||||
|  | ||||
|   // QPIRI values | ||||
|   PIPSOLAR_SENSOR(grid_rating_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(grid_rating_current, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(ac_output_rating_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(ac_output_rating_frequency, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(ac_output_rating_current, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(ac_output_rating_apparent_power, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(ac_output_rating_active_power, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(battery_rating_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(battery_recharge_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(battery_under_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(battery_bulk_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(battery_float_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(battery_type, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(current_max_ac_charging_current, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(current_max_charging_current, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(input_voltage_range, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(output_source_priority, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(charger_source_priority, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(parallel_max_num, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(machine_type, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(topology, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(output_mode, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(battery_redischarge_voltage, QPIRI, float) | ||||
|   PIPSOLAR_SENSOR(pv_ok_condition_for_parallel, QPIRI, int) | ||||
|   PIPSOLAR_SENSOR(pv_power_balance, QPIRI, int) | ||||
|  | ||||
|   // QMOD values | ||||
|   PIPSOLAR_VALUED_TEXT_SENSOR(device_mode, QMOD, char) | ||||
|  | ||||
|   // QFLAG values | ||||
|   PIPSOLAR_BINARY_SENSOR(silence_buzzer_open_buzzer, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(overload_bypass_function, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(lcd_escape_to_default, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(overload_restart_function, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(over_temperature_restart_function, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(backlight_on, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(alarm_on_when_primary_source_interrupt, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_code_record, QFLAG, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(power_saving, QFLAG, int) | ||||
|  | ||||
|   // QPIWS values | ||||
|   PIPSOLAR_BINARY_SENSOR(warnings_present, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(faults_present, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_power_loss, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_inverter_fault, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_bus_over, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_bus_under, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_bus_soft_fail, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_line_fail, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_opvshort, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_inverter_voltage_too_low, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_inverter_voltage_too_high, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_over_temperature, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_fan_lock, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_battery_voltage_high, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_battery_low_alarm, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_battery_under_shutdown, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_battery_derating, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_over_load, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_eeprom_failed, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_inverter_over_current, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_inverter_soft_failed, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_self_test_failed, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_op_dc_voltage_over, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_battery_open, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_current_sensor_failed, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_battery_short, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_power_limit, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_pv_voltage_high, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_mppt_overload, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_mppt_overload, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_battery_too_low_to_charge, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_dc_dc_over_current, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(fault_code, QPIWS, int) | ||||
|   PIPSOLAR_BINARY_SENSOR(warnung_low_pv_energy, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_high_ac_input_during_bus_soft_start, QPIWS, bool) | ||||
|   PIPSOLAR_BINARY_SENSOR(warning_battery_equalization, QPIWS, bool) | ||||
|  | ||||
|   PIPSOLAR_TEXT_SENSOR(last_qpigs, QPIGS) | ||||
|   PIPSOLAR_TEXT_SENSOR(last_qpiri, QPIRI) | ||||
|   PIPSOLAR_TEXT_SENSOR(last_qmod, QMOD) | ||||
|   PIPSOLAR_TEXT_SENSOR(last_qflag, QFLAG) | ||||
|   PIPSOLAR_TEXT_SENSOR(last_qpiws, QPIWS) | ||||
|   PIPSOLAR_TEXT_SENSOR(last_qt, QT) | ||||
|   PIPSOLAR_TEXT_SENSOR(last_qmn, QMN) | ||||
|  | ||||
|   PIPSOLAR_SWITCH(output_source_priority_utility_switch, QPIRI) | ||||
|   PIPSOLAR_SWITCH(output_source_priority_solar_switch, QPIRI) | ||||
|   PIPSOLAR_SWITCH(output_source_priority_battery_switch, QPIRI) | ||||
|   PIPSOLAR_SWITCH(input_voltage_range_switch, QPIRI) | ||||
|   PIPSOLAR_SWITCH(pv_ok_condition_for_parallel_switch, QPIRI) | ||||
|   PIPSOLAR_SWITCH(pv_power_balance_switch, QPIRI) | ||||
|  | ||||
|   void switch_command(const std::string &command); | ||||
|   void setup() override; | ||||
|   void loop() override; | ||||
|   void dump_config() override; | ||||
|   void update() override; | ||||
|  | ||||
|  protected: | ||||
|   static const size_t PIPSOLAR_READ_BUFFER_LENGTH = 110;  // maximum supported answer length | ||||
|   static const size_t COMMAND_QUEUE_LENGTH = 10; | ||||
|   static const size_t COMMAND_TIMEOUT = 5000; | ||||
|   uint32_t last_poll_ = 0; | ||||
|   void add_polling_command_(const char *command, ENUMPollingCommand polling_command); | ||||
|   void empty_uart_buffer_(); | ||||
|   uint8_t check_incoming_crc_(); | ||||
|   uint8_t check_incoming_length_(uint8_t length); | ||||
|   uint16_t calc_crc_(uint8_t *msg, int n); | ||||
|   uint16_t crc_xmodem_update_(uint16_t crc, uint8_t data); | ||||
|   uint8_t send_next_command_(); | ||||
|   void send_next_poll_(); | ||||
|   void queue_command_(const char *command, byte length); | ||||
|   std::string command_queue_[COMMAND_QUEUE_LENGTH]; | ||||
|   uint8_t command_queue_position_ = 0; | ||||
|   uint8_t read_buffer_[PIPSOLAR_READ_BUFFER_LENGTH]; | ||||
|   size_t read_pos_{0}; | ||||
|  | ||||
|   uint32_t command_start_millis_ = 0; | ||||
|   uint8_t state_; | ||||
|   enum State { | ||||
|     STATE_IDLE = 0, | ||||
|     STATE_POLL = 1, | ||||
|     STATE_COMMAND = 2, | ||||
|     STATE_POLL_COMPLETE = 3, | ||||
|     STATE_COMMAND_COMPLETE = 4, | ||||
|     STATE_POLL_CHECKED = 5, | ||||
|     STATE_POLL_DECODED = 6, | ||||
|   }; | ||||
|  | ||||
|   uint8_t last_polling_command_ = 0; | ||||
|   PollingCommand used_polling_commands_[15]; | ||||
| }; | ||||
|  | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
							
								
								
									
										220
									
								
								esphome/components/pipsolar/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								esphome/components/pipsolar/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import sensor | ||||
| from esphome.const import ( | ||||
|     DEVICE_CLASS_CURRENT, | ||||
|     DEVICE_CLASS_EMPTY, | ||||
|     DEVICE_CLASS_POWER, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     DEVICE_CLASS_VOLTAGE, | ||||
|     ICON_CURRENT_AC, | ||||
|     ICON_EMPTY, | ||||
|     UNIT_AMPERE, | ||||
|     UNIT_CELSIUS, | ||||
|     UNIT_HERTZ, | ||||
|     UNIT_PERCENT, | ||||
|     UNIT_VOLT, | ||||
|     UNIT_EMPTY, | ||||
|     UNIT_VOLT_AMPS, | ||||
|     UNIT_WATT, | ||||
|     CONF_BUS_VOLTAGE, | ||||
|     CONF_BATTERY_VOLTAGE, | ||||
| ) | ||||
| from .. import PIPSOLAR_COMPONENT_SCHEMA, CONF_PIPSOLAR_ID | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
|  | ||||
| # QPIRI sensors | ||||
| CONF_GRID_RATING_VOLTAGE = "grid_rating_voltage" | ||||
| CONF_GRID_RATING_CURRENT = "grid_rating_current" | ||||
| CONF_AC_OUTPUT_RATING_VOLTAGE = "ac_output_rating_voltage" | ||||
| CONF_AC_OUTPUT_RATING_FREQUENCY = "ac_output_rating_frequency" | ||||
| CONF_AC_OUTPUT_RATING_CURRENT = "ac_output_rating_current" | ||||
| CONF_AC_OUTPUT_RATING_APPARENT_POWER = "ac_output_rating_apparent_power" | ||||
| CONF_AC_OUTPUT_RATING_ACTIVE_POWER = "ac_output_rating_active_power" | ||||
| CONF_BATTERY_RATING_VOLTAGE = "battery_rating_voltage" | ||||
| CONF_BATTERY_RECHARGE_VOLTAGE = "battery_recharge_voltage" | ||||
| CONF_BATTERY_UNDER_VOLTAGE = "battery_under_voltage" | ||||
| CONF_BATTERY_BULK_VOLTAGE = "battery_bulk_voltage" | ||||
| CONF_BATTERY_FLOAT_VOLTAGE = "battery_float_voltage" | ||||
| CONF_BATTERY_TYPE = "battery_type" | ||||
| CONF_CURRENT_MAX_AC_CHARGING_CURRENT = "current_max_ac_charging_current" | ||||
| CONF_CURRENT_MAX_CHARGING_CURRENT = "current_max_charging_current" | ||||
| CONF_INPUT_VOLTAGE_RANGE = "input_voltage_range" | ||||
| CONF_OUTPUT_SOURCE_PRIORITY = "output_source_priority" | ||||
| CONF_CHARGER_SOURCE_PRIORITY = "charger_source_priority" | ||||
| CONF_PARALLEL_MAX_NUM = "parallel_max_num" | ||||
| CONF_MACHINE_TYPE = "machine_type" | ||||
| CONF_TOPOLOGY = "topology" | ||||
| CONF_OUTPUT_MODE = "output_mode" | ||||
| CONF_BATTERY_REDISCHARGE_VOLTAGE = "battery_redischarge_voltage" | ||||
| CONF_PV_OK_CONDITION_FOR_PARALLEL = "pv_ok_condition_for_parallel" | ||||
| CONF_PV_POWER_BALANCE = "pv_power_balance" | ||||
|  | ||||
| CONF_GRID_VOLTAGE = "grid_voltage" | ||||
| CONF_GRID_FREQUENCY = "grid_frequency" | ||||
| CONF_AC_OUTPUT_VOLTAGE = "ac_output_voltage" | ||||
| CONF_AC_OUTPUT_FREQUENCY = "ac_output_frequency" | ||||
| CONF_AC_OUTPUT_APPARENT_POWER = "ac_output_apparent_power" | ||||
| CONF_AC_OUTPUT_ACTIVE_POWER = "ac_output_active_power" | ||||
| CONF_OUTPUT_LOAD_PERCENT = "output_load_percent" | ||||
| CONF_BATTERY_CHARGING_CURRENT = "battery_charging_current" | ||||
| CONF_BATTERY_CAPACITY_PERCENT = "battery_capacity_percent" | ||||
| CONF_INVERTER_HEAT_SINK_TEMPERATURE = "inverter_heat_sink_temperature" | ||||
| CONF_PV_INPUT_CURRENT_FOR_BATTERY = "pv_input_current_for_battery" | ||||
| CONF_PV_INPUT_VOLTAGE = "pv_input_voltage" | ||||
| CONF_BATTERY_VOLTAGE_SCC = "battery_voltage_scc" | ||||
| CONF_BATTERY_DISCHARGE_CURRENT = "battery_discharge_current" | ||||
| CONF_ADD_SBU_PRIORITY_VERSION = "add_sbu_priority_version" | ||||
| CONF_CONFIGURATION_STATUS = "configuration_status" | ||||
| CONF_SCC_FIRMWARE_VERSION = "scc_firmware_version" | ||||
| CONF_BATTERY_VOLTAGE_OFFSET_FOR_FANS_ON = "battery_voltage_offset_for_fans_on" | ||||
| CONF_EEPROM_VERSION = "eeprom_version" | ||||
| CONF_PV_CHARGING_POWER = "pv_charging_power" | ||||
|  | ||||
| TYPES = { | ||||
|     CONF_GRID_RATING_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_GRID_RATING_CURRENT: sensor.sensor_schema( | ||||
|         UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_RATING_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_RATING_FREQUENCY: sensor.sensor_schema( | ||||
|         UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_RATING_CURRENT: sensor.sensor_schema( | ||||
|         UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_RATING_APPARENT_POWER: sensor.sensor_schema( | ||||
|         UNIT_VOLT_AMPS, ICON_EMPTY, 1, DEVICE_CLASS_POWER | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_RATING_ACTIVE_POWER: sensor.sensor_schema( | ||||
|         UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER | ||||
|     ), | ||||
|     CONF_BATTERY_RATING_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_RECHARGE_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_UNDER_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_BULK_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_FLOAT_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_TYPE: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_CURRENT_MAX_AC_CHARGING_CURRENT: sensor.sensor_schema( | ||||
|         UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT | ||||
|     ), | ||||
|     CONF_CURRENT_MAX_CHARGING_CURRENT: sensor.sensor_schema( | ||||
|         UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT | ||||
|     ), | ||||
|     CONF_INPUT_VOLTAGE_RANGE: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_OUTPUT_SOURCE_PRIORITY: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_CHARGER_SOURCE_PRIORITY: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_PARALLEL_MAX_NUM: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_MACHINE_TYPE: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_TOPOLOGY: sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY), | ||||
|     CONF_OUTPUT_MODE: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_BATTERY_REDISCHARGE_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_PV_OK_CONDITION_FOR_PARALLEL: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_PV_POWER_BALANCE: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_GRID_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_GRID_FREQUENCY: sensor.sensor_schema( | ||||
|         UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_FREQUENCY: sensor.sensor_schema( | ||||
|         UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_APPARENT_POWER: sensor.sensor_schema( | ||||
|         UNIT_VOLT_AMPS, ICON_EMPTY, 1, DEVICE_CLASS_POWER | ||||
|     ), | ||||
|     CONF_AC_OUTPUT_ACTIVE_POWER: sensor.sensor_schema( | ||||
|         UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER | ||||
|     ), | ||||
|     CONF_OUTPUT_LOAD_PERCENT: sensor.sensor_schema( | ||||
|         UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_BUS_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_CHARGING_CURRENT: sensor.sensor_schema( | ||||
|         UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT | ||||
|     ), | ||||
|     CONF_BATTERY_CAPACITY_PERCENT: sensor.sensor_schema( | ||||
|         UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_INVERTER_HEAT_SINK_TEMPERATURE: sensor.sensor_schema( | ||||
|         UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE | ||||
|     ), | ||||
|     CONF_PV_INPUT_CURRENT_FOR_BATTERY: sensor.sensor_schema( | ||||
|         UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT | ||||
|     ), | ||||
|     CONF_PV_INPUT_VOLTAGE: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_VOLTAGE_SCC: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_BATTERY_DISCHARGE_CURRENT: sensor.sensor_schema( | ||||
|         UNIT_AMPERE, ICON_EMPTY, 1, DEVICE_CLASS_CURRENT | ||||
|     ), | ||||
|     CONF_BATTERY_VOLTAGE_OFFSET_FOR_FANS_ON: sensor.sensor_schema( | ||||
|         UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE | ||||
|     ), | ||||
|     CONF_EEPROM_VERSION: sensor.sensor_schema( | ||||
|         UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY | ||||
|     ), | ||||
|     CONF_PV_CHARGING_POWER: sensor.sensor_schema( | ||||
|         UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER | ||||
|     ), | ||||
| } | ||||
|  | ||||
| CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend( | ||||
|     {cv.Optional(type): schema for type, schema in TYPES.items()} | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     paren = await cg.get_variable(config[CONF_PIPSOLAR_ID]) | ||||
|  | ||||
|     for type, _ in TYPES.items(): | ||||
|         if type in config: | ||||
|             conf = config[type] | ||||
|             sens = await sensor.new_sensor(conf) | ||||
|             cg.add(getattr(paren, f"set_{type}")(sens)) | ||||
							
								
								
									
										60
									
								
								esphome/components/pipsolar/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								esphome/components/pipsolar/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import switch | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_INVERTED, | ||||
|     CONF_ICON, | ||||
|     ICON_POWER, | ||||
| ) | ||||
| from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA, pipsolar_ns | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
|  | ||||
| CONF_OUTPUT_SOURCE_PRIORITY_UTILITY = "output_source_priority_utility" | ||||
| CONF_OUTPUT_SOURCE_PRIORITY_SOLAR = "output_source_priority_solar" | ||||
| CONF_OUTPUT_SOURCE_PRIORITY_BATTERY = "output_source_priority_battery" | ||||
| CONF_INPUT_VOLTAGE_RANGE = "input_voltage_range" | ||||
| CONF_PV_OK_CONDITION_FOR_PARALLEL = "pv_ok_condition_for_parallel" | ||||
| CONF_PV_POWER_BALANCE = "pv_power_balance" | ||||
|  | ||||
| TYPES = { | ||||
|     CONF_OUTPUT_SOURCE_PRIORITY_UTILITY: ("POP00", None), | ||||
|     CONF_OUTPUT_SOURCE_PRIORITY_SOLAR: ("POP01", None), | ||||
|     CONF_OUTPUT_SOURCE_PRIORITY_BATTERY: ("POP02", None), | ||||
|     CONF_INPUT_VOLTAGE_RANGE: ("PGR01", "PGR00"), | ||||
|     CONF_PV_OK_CONDITION_FOR_PARALLEL: ("PPVOKC1", "PPVOKC0"), | ||||
|     CONF_PV_POWER_BALANCE: ("PSPB1", "PSPB0"), | ||||
| } | ||||
|  | ||||
| PipsolarSwitch = pipsolar_ns.class_("PipsolarSwitch", switch.Switch, cg.Component) | ||||
|  | ||||
| PIPSWITCH_SCHEMA = switch.SWITCH_SCHEMA.extend( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(PipsolarSwitch), | ||||
|         cv.Optional(CONF_INVERTED): cv.invalid( | ||||
|             "Pipsolar switches do not support inverted mode!" | ||||
|         ), | ||||
|         cv.Optional(CONF_ICON, default=ICON_POWER): switch.icon, | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
| CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend( | ||||
|     {cv.Optional(type): PIPSWITCH_SCHEMA for type in TYPES} | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     paren = await cg.get_variable(config[CONF_PIPSOLAR_ID]) | ||||
|  | ||||
|     for type, (on, off) in TYPES.items(): | ||||
|         if type in config: | ||||
|             conf = config[type] | ||||
|             var = cg.new_Pvariable(conf[CONF_ID]) | ||||
|             await cg.register_component(var, conf) | ||||
|             await switch.register_switch(var, conf) | ||||
|             cg.add(getattr(paren, f"set_{type}_switch")(var)) | ||||
|             cg.add(var.set_parent(paren)) | ||||
|             cg.add(var.set_on_command(on)) | ||||
|             if off is not None: | ||||
|                 cg.add(var.set_off_command(off)) | ||||
							
								
								
									
										24
									
								
								esphome/components/pipsolar/switch/pipsolar_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/pipsolar/switch/pipsolar_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| #include "pipsolar_switch.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
|  | ||||
| static const char *const TAG = "pipsolar.switch"; | ||||
|  | ||||
| void PipsolarSwitch::dump_config() { LOG_SWITCH("", "Pipsolar Switch", this); } | ||||
| void PipsolarSwitch::write_state(bool state) { | ||||
|   if (state) { | ||||
|     if (this->on_command_.length() > 0) { | ||||
|       this->parent_->switch_command(this->on_command_); | ||||
|     } | ||||
|   } else { | ||||
|     if (this->off_command_.length() > 0) { | ||||
|       this->parent_->switch_command(this->off_command_); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
							
								
								
									
										25
									
								
								esphome/components/pipsolar/switch/pipsolar_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/pipsolar/switch/pipsolar_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../pipsolar.h" | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
| class Pipsolar; | ||||
| class PipsolarSwitch : public switch_::Switch, public Component { | ||||
|  public: | ||||
|   void set_parent(Pipsolar *parent) { this->parent_ = parent; }; | ||||
|   void set_on_command(std::string command) { this->on_command_ = std::move(command); }; | ||||
|   void set_off_command(std::string command) { this->off_command_ = std::move(command); }; | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
|   std::string on_command_; | ||||
|   std::string off_command_; | ||||
|   Pipsolar *parent_; | ||||
| }; | ||||
|  | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
							
								
								
									
										52
									
								
								esphome/components/pipsolar/text_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								esphome/components/pipsolar/text_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import text_sensor | ||||
| from esphome.const import CONF_ID | ||||
| from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA, pipsolar_ns | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
|  | ||||
| CONF_DEVICE_MODE = "device_mode" | ||||
| CONF_LAST_QPIGS = "last_qpigs" | ||||
| CONF_LAST_QPIRI = "last_qpiri" | ||||
| CONF_LAST_QMOD = "last_qmod" | ||||
| CONF_LAST_QFLAG = "last_qflag" | ||||
| CONF_LAST_QPIWS = "last_qpiws" | ||||
| CONF_LAST_QT = "last_qt" | ||||
| CONF_LAST_QMN = "last_qmn" | ||||
|  | ||||
| PipsolarTextSensor = pipsolar_ns.class_( | ||||
|     "PipsolarTextSensor", text_sensor.TextSensor, cg.Component | ||||
| ) | ||||
|  | ||||
| TYPES = [ | ||||
|     CONF_DEVICE_MODE, | ||||
|     CONF_LAST_QPIGS, | ||||
|     CONF_LAST_QPIRI, | ||||
|     CONF_LAST_QMOD, | ||||
|     CONF_LAST_QFLAG, | ||||
|     CONF_LAST_QPIWS, | ||||
|     CONF_LAST_QT, | ||||
|     CONF_LAST_QMN, | ||||
| ] | ||||
|  | ||||
| CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend( | ||||
|     { | ||||
|         cv.Optional(type): text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|             {cv.GenerateID(): cv.declare_id(PipsolarTextSensor)} | ||||
|         ) | ||||
|         for type in TYPES | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     paren = await cg.get_variable(config[CONF_PIPSOLAR_ID]) | ||||
|  | ||||
|     for type in TYPES: | ||||
|         if type in config: | ||||
|             conf = config[type] | ||||
|             var = cg.new_Pvariable(conf[CONF_ID]) | ||||
|             await text_sensor.register_text_sensor(var, conf) | ||||
|             await cg.register_component(var, conf) | ||||
|             cg.add(getattr(paren, f"set_{type}")(var)) | ||||
| @@ -0,0 +1,13 @@ | ||||
| #include "pipsolar_textsensor.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
|  | ||||
| static const char *const TAG = "pipsolar.text_sensor"; | ||||
|  | ||||
| void PipsolarTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Pipsolar TextSensor", this); } | ||||
|  | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,20 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../pipsolar.h" | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
| #include "esphome/core/component.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pipsolar { | ||||
| class Pipsolar; | ||||
| class PipsolarTextSensor : public Component, public text_sensor::TextSensor { | ||||
|  public: | ||||
|   void set_parent(Pipsolar *parent) { this->parent_ = parent; }; | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   Pipsolar *parent_; | ||||
| }; | ||||
|  | ||||
| }  // namespace pipsolar | ||||
| }  // namespace esphome | ||||
							
								
								
									
										228
									
								
								tests/test4.yaml
									
									
									
									
									
								
							
							
						
						
									
										228
									
								
								tests/test4.yaml
									
									
									
									
									
								
							| @@ -56,6 +56,9 @@ time: | ||||
| tuya: | ||||
|   time_id: sntp_time | ||||
|  | ||||
| pipsolar: | ||||
|     id: inverter0 | ||||
|  | ||||
| sensor: | ||||
|   - platform: homeassistant | ||||
|     entity_id: sensor.hello_world | ||||
| @@ -63,6 +66,140 @@ sensor: | ||||
|   - platform: tuya | ||||
|     id: tuya_sensor | ||||
|     sensor_datapoint: 1 | ||||
|   - platform: pipsolar | ||||
|     pipsolar_id: inverter0 | ||||
|     grid_rating_voltage: | ||||
|       id: inverter0_grid_rating_voltage | ||||
|       name: inverter0_grid_rating_voltage | ||||
|     grid_rating_current: | ||||
|       id: inverter0_grid_rating_current | ||||
|       name: inverter0_grid_rating_current | ||||
|     ac_output_rating_voltage: | ||||
|       id: inverter0_ac_output_rating_voltage | ||||
|       name: inverter0_ac_output_rating_voltage | ||||
|     ac_output_rating_frequency: | ||||
|       id: inverter0_ac_output_rating_frequency | ||||
|       name: inverter0_ac_output_rating_frequency | ||||
|     ac_output_rating_current: | ||||
|       id: inverter0_ac_output_rating_current | ||||
|       name: inverter0_ac_output_rating_current | ||||
|     ac_output_rating_apparent_power: | ||||
|       id: inverter0_ac_output_rating_apparent_power | ||||
|       name: inverter0_ac_output_rating_apparent_power | ||||
|     ac_output_rating_active_power: | ||||
|       id: inverter0_ac_output_rating_active_power | ||||
|       name: inverter0_ac_output_rating_active_power | ||||
|     battery_rating_voltage: | ||||
|       id: inverter0_battery_rating_voltage | ||||
|       name: inverter0_battery_rating_voltage | ||||
|     battery_recharge_voltage: | ||||
|       id: inverter0_battery_recharge_voltage | ||||
|       name: inverter0_battery_recharge_voltage | ||||
|     battery_under_voltage: | ||||
|       id: inverter0_battery_under_voltage | ||||
|       name: inverter0_battery_under_voltage | ||||
|     battery_bulk_voltage: | ||||
|       id: inverter0_battery_bulk_voltage | ||||
|       name: inverter0_battery_bulk_voltage | ||||
|     battery_float_voltage: | ||||
|       id: inverter0_battery_float_voltage | ||||
|       name: inverter0_battery_float_voltage | ||||
|     battery_type: | ||||
|       id: inverter0_battery_type | ||||
|       name: inverter0_battery_type | ||||
|     current_max_ac_charging_current: | ||||
|       id: inverter0_current_max_ac_charging_current | ||||
|       name: inverter0_current_max_ac_charging_current | ||||
|     current_max_charging_current: | ||||
|       id: inverter0_current_max_charging_current | ||||
|       name: inverter0_current_max_charging_current | ||||
|     input_voltage_range: | ||||
|       id: inverter0_input_voltage_range | ||||
|       name: inverter0_input_voltage_range | ||||
|     output_source_priority: | ||||
|       id: inverter0_output_source_priority | ||||
|       name: inverter0_output_source_priority | ||||
|     charger_source_priority: | ||||
|       id: inverter0_charger_source_priority | ||||
|       name: inverter0_charger_source_priority | ||||
|     parallel_max_num: | ||||
|       id: inverter0_parallel_max_num | ||||
|       name: inverter0_parallel_max_num | ||||
|     machine_type: | ||||
|       id: inverter0_machine_type | ||||
|       name: inverter0_machine_type | ||||
|     topology: | ||||
|       id: inverter0_topology | ||||
|       name: inverter0_topology | ||||
|     output_mode: | ||||
|       id: inverter0_output_mode | ||||
|       name: inverter0_output_mode | ||||
|     battery_redischarge_voltage: | ||||
|       id: inverter0_battery_redischarge_voltage | ||||
|       name: inverter0_battery_redischarge_voltage | ||||
|     pv_ok_condition_for_parallel: | ||||
|       id: inverter0_pv_ok_condition_for_parallel | ||||
|       name: inverter0_pv_ok_condition_for_parallel | ||||
|     pv_power_balance: | ||||
|       id: inverter0_pv_power_balance | ||||
|       name: inverter0_pv_power_balance | ||||
|     grid_voltage: | ||||
|       id: inverter0_grid_voltage | ||||
|       name: inverter0_grid_voltage | ||||
|     grid_frequency: | ||||
|       id: inverter0_grid_frequency | ||||
|       name: inverter0_grid_frequency | ||||
|     ac_output_voltage: | ||||
|       id: inverter0_ac_output_voltage | ||||
|       name: inverter0_ac_output_voltage | ||||
|     ac_output_frequency: | ||||
|       id: inverter0_ac_output_frequency | ||||
|       name: inverter0_ac_output_frequency | ||||
|     ac_output_apparent_power: | ||||
|       id: inverter0_ac_output_apparent_power | ||||
|       name: inverter0_ac_output_apparent_power | ||||
|     ac_output_active_power: | ||||
|       id: inverter0_ac_output_active_power | ||||
|       name: inverter0_ac_output_active_power | ||||
|     output_load_percent: | ||||
|       id: inverter0_output_load_percent | ||||
|       name: inverter0_output_load_percent | ||||
|     bus_voltage: | ||||
|       id: inverter0_bus_voltage | ||||
|       name: inverter0_bus_voltage | ||||
|     battery_voltage: | ||||
|       id: inverter0_battery_voltage | ||||
|       name: inverter0_battery_voltage | ||||
|     battery_charging_current: | ||||
|       id: inverter0_battery_charging_current | ||||
|       name: inverter0_battery_charging_current | ||||
|     battery_capacity_percent: | ||||
|       id: inverter0_battery_capacity_percent | ||||
|       name: inverter0_battery_capacity_percent | ||||
|     inverter_heat_sink_temperature: | ||||
|       id: inverter0_inverter_heat_sink_temperature | ||||
|       name: inverter0_inverter_heat_sink_temperature | ||||
|     pv_input_current_for_battery: | ||||
|       id: inverter0_pv_input_current_for_battery | ||||
|       name: inverter0_pv_input_current_for_battery | ||||
|     pv_input_voltage: | ||||
|       id: inverter0_pv_input_voltage | ||||
|       name: inverter0_pv_input_voltage | ||||
|     battery_voltage_scc: | ||||
|       id: inverter0_battery_voltage_scc | ||||
|       name: inverter0_battery_voltage_scc | ||||
|     battery_discharge_current: | ||||
|       id: inverter0_battery_discharge_current | ||||
|       name: inverter0_battery_discharge_current | ||||
|     battery_voltage_offset_for_fans_on: | ||||
|       id: inverter0_battery_voltage_offset_for_fans_on | ||||
|       name: inverter0_battery_voltage_offset_for_fans_on | ||||
|     eeprom_version: | ||||
|       id: inverter0_eeprom_version | ||||
|       name: inverter0_eeprom_version | ||||
|     pv_charging_power: | ||||
|       id: inverter0_pv_charging_power | ||||
|       name: inverter0_pv_charging_power | ||||
|   - platform: "hrxl_maxsonar_wr" | ||||
|     name: "Rainwater Tank Level" | ||||
|     filters: | ||||
| @@ -95,6 +232,59 @@ binary_sensor: | ||||
|   - platform: tuya | ||||
|     id: tuya_binary_sensor | ||||
|     sensor_datapoint: 1 | ||||
|   - platform: pipsolar | ||||
|     pipsolar_id: inverter0 | ||||
|     add_sbu_priority_version: | ||||
|       id: inverter0_add_sbu_priority_version | ||||
|       name: inverter0_add_sbu_priority_version | ||||
|     configuration_status: | ||||
|       id: inverter0_configuration_status | ||||
|       name: inverter0_configuration_status | ||||
|     scc_firmware_version: | ||||
|       id: inverter0_scc_firmware_version | ||||
|       name: inverter0_scc_firmware_version | ||||
|     load_status: | ||||
|       id: inverter0_load_status | ||||
|       name: inverter0_load_status | ||||
|     battery_voltage_to_steady_while_charging: | ||||
|       id: inverter0_battery_voltage_to_steady_while_charging | ||||
|       name: inverter0_battery_voltage_to_steady_while_charging | ||||
|     charging_status: | ||||
|       id: inverter0_charging_status | ||||
|       name: inverter0_charging_status | ||||
|     scc_charging_status: | ||||
|       id: inverter0_scc_charging_status | ||||
|       name: inverter0_scc_charging_status | ||||
|     ac_charging_status: | ||||
|       id: inverter0_ac_charging_status | ||||
|       name: inverter0_ac_charging_status | ||||
|     charging_to_floating_mode: | ||||
|       id: inverter0_charging_to_floating_mode | ||||
|       name: inverter0_charging_to_floating_mode | ||||
|     switch_on: | ||||
|       id: inverter0_switch_on | ||||
|       name: inverter0_switch_on | ||||
|     dustproof_installed: | ||||
|       id: inverter0_dustproof_installed | ||||
|       name: inverter0_dustproof_installed | ||||
|     silence_buzzer_open_buzzer: | ||||
|       id: inverter0_silence_buzzer_open_buzzer | ||||
|       name: inverter0_silence_buzzer_open_buzzer | ||||
|     overload_bypass_function: | ||||
|       id: inverter0_overload_bypass_function | ||||
|       name: inverter0_overload_bypass_function | ||||
|     lcd_escape_to_default: | ||||
|       id: inverter0_lcd_escape_to_default | ||||
|       name: inverter0_lcd_escape_to_default | ||||
|     overload_restart_function: | ||||
|       id: inverter0_overload_restart_function | ||||
|       name: inverter0_overload_restart_function | ||||
|     over_temperature_restart_function: | ||||
|       id: inverter0_over_temperature_restart_function | ||||
|       name: inverter0_over_temperature_restart_function | ||||
|     backlight_on: | ||||
|       id: inverter0_backlight_on | ||||
|       name: inverter0_backlight_on | ||||
|   - platform: template | ||||
|     id: ar1 | ||||
|     lambda: 'return {};' | ||||
| @@ -131,6 +321,20 @@ switch: | ||||
|   - platform: tuya | ||||
|     id: tuya_switch | ||||
|     switch_datapoint: 1 | ||||
|   - platform: pipsolar | ||||
|     pipsolar_id: inverter0 | ||||
|     output_source_priority_utility: | ||||
|       name: inverter0_output_source_priority_utility | ||||
|     output_source_priority_solar: | ||||
|       name: inverter0_output_source_priority_solar | ||||
|     output_source_priority_battery: | ||||
|       name: inverter0_output_source_priority_battery | ||||
|     input_voltage_range: | ||||
|       name: inverter0_input_voltage_range | ||||
|     pv_ok_condition_for_parallel: | ||||
|       name: inverter0_pv_ok_condition_for_parallel | ||||
|     pv_power_balance: | ||||
|       name: inverter0_pv_power_balance | ||||
|  | ||||
| light: | ||||
|   - platform: fastled_clockless | ||||
| @@ -204,6 +408,30 @@ display: | ||||
|     lambda: |- | ||||
|       it.rectangle(0, 0, it.get_width(), it.get_height()); | ||||
|  | ||||
| text_sensor: | ||||
|   - platform: pipsolar | ||||
|     pipsolar_id: inverter0 | ||||
|     device_mode: | ||||
|       id: inverter0_device_mode | ||||
|       name: inverter0_device_mode | ||||
|     last_qpigs: | ||||
|       id: inverter0_last_qpigs | ||||
|       name: inverter0_last_qpigs | ||||
|     last_qpiri: | ||||
|       id: inverter0_last_qpiri | ||||
|       name: inverter0_last_qpiri | ||||
|     last_qmod: | ||||
|       id: inverter0_last_qmod | ||||
|       name: inverter0_last_qmod | ||||
|     last_qflag: | ||||
|       id: inverter0_last_qflag | ||||
|       name: inverter0_last_qflag | ||||
|  | ||||
| output: | ||||
|   - platform: pipsolar | ||||
|     pipsolar_id: inverter0 | ||||
|     battery_recharge_voltage: | ||||
|       id: inverter0_battery_recharge_voltage_out | ||||
| esp32_camera: | ||||
|   name: ESP-32 Camera | ||||
|   data_pins: [GPIO17, GPIO35, GPIO34, GPIO5, GPIO39, GPIO18, GPIO36, GPIO19] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user