ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
pipsolar.cpp
Go to the documentation of this file.
1#include "pipsolar.h"
3#include "esphome/core/log.h"
4
5namespace esphome {
6namespace pipsolar {
7
8static const char *const TAG = "pipsolar";
9
10void Pipsolar::setup() {
11 this->state_ = STATE_IDLE;
12 this->command_start_millis_ = 0;
13}
14
16 uint8_t buf[64];
17 size_t avail;
18 while ((avail = this->available()) > 0) {
19 if (!this->read_array(buf, std::min(avail, sizeof(buf)))) {
20 break;
21 }
22 }
23}
24
25void Pipsolar::loop() {
26 // Read message
27 if (this->state_ == STATE_IDLE) {
28 this->empty_uart_buffer_();
29
30 if (this->send_next_command_()) {
31 // command sent
32 return;
33 }
34
35 if (this->send_next_poll_()) {
36 // poll sent
37 return;
38 }
39
40 return;
41 }
42 if (this->state_ == STATE_COMMAND_COMPLETE) {
43 if (this->check_incoming_length_(4)) {
44 if (this->check_incoming_crc_()) {
45 // crc ok
46 if (this->read_buffer_[1] == 'A' && this->read_buffer_[2] == 'C' && this->read_buffer_[3] == 'K') {
47 ESP_LOGD(TAG, "command successful");
48 } else {
49 ESP_LOGD(TAG, "command not successful");
50 }
51 this->command_queue_[this->command_queue_position_] = std::string("");
53 this->state_ = STATE_IDLE;
54 } else {
55 // crc failed
56 // no log message necessary, check_incoming_crc_() logs
57 this->command_queue_[this->command_queue_position_] = std::string("");
59 this->state_ = STATE_IDLE;
60 }
61 } else {
62 ESP_LOGD(TAG, "command %s response length not OK: with length %zu",
63 this->command_queue_[this->command_queue_position_].c_str(), this->read_pos_);
64 this->command_queue_[this->command_queue_position_] = std::string("");
66 this->state_ = STATE_IDLE;
67 }
68 }
69
70 if (this->state_ == STATE_POLL_CHECKED) {
71 ESP_LOGD(TAG, "poll %s decode", this->enabled_polling_commands_[this->last_polling_command_].command);
73 (const char *) this->read_buffer_);
74 this->state_ = STATE_IDLE;
75 return;
76 }
77
78 if (this->state_ == STATE_POLL_COMPLETE) {
79 if (this->check_incoming_crc_()) {
80 if (this->read_buffer_[0] == '(' && this->read_buffer_[1] == 'N' && this->read_buffer_[2] == 'A' &&
81 this->read_buffer_[3] == 'K') {
82 ESP_LOGD(TAG, "poll %s NACK", this->enabled_polling_commands_[this->last_polling_command_].command);
84 this->state_ = STATE_IDLE;
85 return;
86 }
87 // crc ok
90 return;
91 } else {
92 // crc failed
93 // no log message necessary, check_incoming_crc_() logs
95 this->state_ = STATE_IDLE;
96 }
97 }
98
99 if (this->state_ == STATE_COMMAND || this->state_ == STATE_POLL) {
100 size_t avail = this->available();
101 while (avail > 0) {
102 uint8_t buf[64];
103 size_t to_read = std::min(avail, sizeof(buf));
104 if (!this->read_array(buf, to_read)) {
105 break;
106 }
107 avail -= to_read;
108 bool done = false;
109 for (size_t i = 0; i < to_read; i++) {
110 uint8_t byte = buf[i];
111
112 // make sure data and null terminator fit in buffer
113 if (this->read_pos_ >= PIPSOLAR_READ_BUFFER_LENGTH - 1) {
114 this->read_pos_ = 0;
115 this->empty_uart_buffer_();
116 ESP_LOGW(TAG, "response data too long, discarding.");
117 done = true;
118 break;
119 }
120 this->read_buffer_[this->read_pos_] = byte;
121 this->read_pos_++;
122
123 // end of answer
124 if (byte == 0x0D) {
125 this->read_buffer_[this->read_pos_] = 0;
126 this->empty_uart_buffer_();
127 if (this->state_ == STATE_POLL) {
129 }
130 if (this->state_ == STATE_COMMAND) {
132 }
133 done = true;
134 break;
135 }
136 }
137 if (done) {
138 break;
139 }
140 }
141 }
142 if (this->state_ == STATE_COMMAND) {
144 // command timeout
145 const char *command = this->command_queue_[this->command_queue_position_].c_str();
147 ESP_LOGD(TAG, "command %s timeout", command);
148 this->command_queue_[this->command_queue_position_] = std::string("");
150 this->state_ = STATE_IDLE;
151 return;
152 }
153 }
154 if (this->state_ == STATE_POLL) {
156 // command timeout
157 ESP_LOGD(TAG, "poll %s timeout", this->enabled_polling_commands_[this->last_polling_command_].command);
159 this->state_ = STATE_IDLE;
160 }
161 }
162}
163
165 if (this->read_pos_ - 3 == length) {
166 return 1;
167 }
168 return 0;
169}
170
172 uint16_t crc16;
174 if (((uint8_t) ((crc16) >> 8)) == read_buffer_[read_pos_ - 3] &&
175 ((uint8_t) ((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) {
176 ESP_LOGD(TAG, "CRC OK");
177 read_buffer_[read_pos_ - 1] = 0;
178 read_buffer_[read_pos_ - 2] = 0;
179 read_buffer_[read_pos_ - 3] = 0;
180 return 1;
181 }
182 ESP_LOGD(TAG, "CRC NOK expected: %X %X but got: %X %X", ((uint8_t) ((crc16) >> 8)), ((uint8_t) ((crc16) &0xff)),
184 return 0;
185}
186
187// send next command from queue
189 uint16_t crc16;
190 if (!this->command_queue_[this->command_queue_position_].empty()) {
191 const char *command = this->command_queue_[this->command_queue_position_].c_str();
192 uint8_t byte_command[16];
193 uint8_t length = this->command_queue_[this->command_queue_position_].length();
194 for (uint8_t i = 0; i < length; i++) {
195 byte_command[i] = (uint8_t) this->command_queue_[this->command_queue_position_].at(i);
196 }
197 this->state_ = STATE_COMMAND;
199 this->empty_uart_buffer_();
200 this->read_pos_ = 0;
201 crc16 = this->pipsolar_crc_(byte_command, length);
202 this->write_str(command);
203 // checksum
204 this->write(((uint8_t) ((crc16) >> 8))); // highbyte
205 this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
206 // end Byte
207 this->write(0x0D);
208 ESP_LOGD(TAG, "Sending command from queue: %s with length %d", command, length);
209 return true;
210 }
211 return false;
212}
213
215 uint16_t crc16;
216 for (uint8_t i = 0; i < POLLING_COMMANDS_MAX; i++) {
217 this->last_polling_command_ = (this->last_polling_command_ + 1) % POLLING_COMMANDS_MAX;
219 // not enabled
220 continue;
221 }
222 if (!this->enabled_polling_commands_[this->last_polling_command_].needs_update) {
223 // no update requested
224 continue;
225 }
226 this->state_ = STATE_POLL;
228 this->empty_uart_buffer_();
229 this->read_pos_ = 0;
230 crc16 = this->pipsolar_crc_(this->enabled_polling_commands_[this->last_polling_command_].command,
234 // checksum
235 this->write(((uint8_t) ((crc16) >> 8))); // highbyte
236 this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
237 // end Byte
238 this->write(0x0D);
239 ESP_LOGD(TAG, "Sending polling command: %s with length %d",
242 return true;
243 }
244 return false;
245}
246
247void Pipsolar::queue_command(const std::string &command) {
248 uint8_t next_position = command_queue_position_;
249 for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) {
250 uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH;
251 if (command_queue_[testposition].empty()) {
252 command_queue_[testposition] = command;
253 ESP_LOGD(TAG, "Command queued successfully: %s at position %d", command.c_str(), testposition);
254 return;
255 }
256 }
257 ESP_LOGD(TAG, "Command queue full dropping command: %s", command.c_str());
258}
259
261 switch (polling_command) {
262 case POLLING_QPIRI:
264 break;
265 case POLLING_QPIGS:
267 break;
268 case POLLING_QMOD:
270 break;
271 case POLLING_QFLAG:
273 break;
274 case POLLING_QPIWS:
276 break;
277 case POLLING_QT:
279 break;
280 case POLLING_QMN:
282 break;
283 default:
284 break;
285 }
286}
288 // handlers are designed in a way that an empty message sets all sensors to unknown
289 this->handle_poll_response_(polling_command, "");
290}
291
293 if (this->last_qpiri_) {
294 this->last_qpiri_->publish_state(message);
295 }
296
297 size_t pos = 0;
298 this->skip_start_(message, &pos);
299
300 this->read_float_sensor_(message, &pos, this->grid_rating_voltage_);
301 this->read_float_sensor_(message, &pos, this->grid_rating_current_);
302 this->read_float_sensor_(message, &pos, this->ac_output_rating_voltage_);
303 this->read_float_sensor_(message, &pos, this->ac_output_rating_frequency_);
304 this->read_float_sensor_(message, &pos, this->ac_output_rating_current_);
305
306 this->read_int_sensor_(message, &pos, this->ac_output_rating_apparent_power_);
307 this->read_int_sensor_(message, &pos, this->ac_output_rating_active_power_);
308
309 this->read_float_sensor_(message, &pos, this->battery_rating_voltage_);
310 this->read_float_sensor_(message, &pos, this->battery_recharge_voltage_);
311 this->read_float_sensor_(message, &pos, this->battery_under_voltage_);
312 this->read_float_sensor_(message, &pos, this->battery_bulk_voltage_);
313 this->read_float_sensor_(message, &pos, this->battery_float_voltage_);
314
315 this->read_int_sensor_(message, &pos, this->battery_type_);
316 this->read_int_sensor_(message, &pos, this->current_max_ac_charging_current_);
317 this->read_int_sensor_(message, &pos, this->current_max_charging_current_);
318
319 esphome::optional<int> input_voltage_range = parse_number<int32_t>(this->read_field_(message, &pos));
320 esphome::optional<int> output_source_priority = parse_number<int32_t>(this->read_field_(message, &pos));
321
322 this->read_int_sensor_(message, &pos, this->charger_source_priority_);
323 this->read_int_sensor_(message, &pos, this->parallel_max_num_);
324 this->read_int_sensor_(message, &pos, this->machine_type_);
325 this->read_int_sensor_(message, &pos, this->topology_);
326 this->read_int_sensor_(message, &pos, this->output_mode_);
327
328 this->read_float_sensor_(message, &pos, this->battery_redischarge_voltage_);
329
330 esphome::optional<int> pv_ok_condition_for_parallel = parse_number<int32_t>(this->read_field_(message, &pos));
331 esphome::optional<int> pv_power_balance = parse_number<int32_t>(this->read_field_(message, &pos));
332
333 if (this->input_voltage_range_) {
334 this->input_voltage_range_->publish_state(input_voltage_range.value_or(NAN));
335 }
336 // special for input voltage range switch
337 if (this->input_voltage_range_switch_ && input_voltage_range.has_value()) {
338 this->input_voltage_range_switch_->publish_state(input_voltage_range.value() == 1);
339 }
340
341 if (this->output_source_priority_) {
342 this->output_source_priority_->publish_state(output_source_priority.value_or(NAN));
343 }
344 // special for output source priority switches
345 if (this->output_source_priority_utility_switch_ && output_source_priority.has_value()) {
346 this->output_source_priority_utility_switch_->publish_state(output_source_priority.value() == 0);
347 }
348 if (this->output_source_priority_solar_switch_ && output_source_priority.has_value()) {
349 this->output_source_priority_solar_switch_->publish_state(output_source_priority.value() == 1);
350 }
351 if (this->output_source_priority_battery_switch_ && output_source_priority.has_value()) {
352 this->output_source_priority_battery_switch_->publish_state(output_source_priority.value() == 2);
353 }
354 if (this->output_source_priority_hybrid_switch_ && output_source_priority.has_value()) {
355 this->output_source_priority_hybrid_switch_->publish_state(output_source_priority.value() == 3);
356 }
357
358 if (this->pv_ok_condition_for_parallel_) {
359 this->pv_ok_condition_for_parallel_->publish_state(pv_ok_condition_for_parallel.value_or(NAN));
360 }
361 // special for pv ok condition switch
362 if (this->pv_ok_condition_for_parallel_switch_ && pv_ok_condition_for_parallel.has_value()) {
363 this->pv_ok_condition_for_parallel_switch_->publish_state(pv_ok_condition_for_parallel.value() == 1);
364 }
365
366 if (this->pv_power_balance_) {
367 this->pv_power_balance_->publish_state(pv_power_balance.value_or(NAN));
368 }
369 // special for power balance switch
370 if (this->pv_power_balance_switch_ && pv_power_balance.has_value()) {
371 this->pv_power_balance_switch_->publish_state(pv_power_balance.value() == 1);
372 }
373}
374
376 if (this->last_qpigs_) {
377 this->last_qpigs_->publish_state(message);
378 }
379
380 size_t pos = 0;
381 this->skip_start_(message, &pos);
382
383 this->read_float_sensor_(message, &pos, this->grid_voltage_);
384 this->read_float_sensor_(message, &pos, this->grid_frequency_);
385 this->read_float_sensor_(message, &pos, this->ac_output_voltage_);
386 this->read_float_sensor_(message, &pos, this->ac_output_frequency_);
387
388 this->read_int_sensor_(message, &pos, this->ac_output_apparent_power_);
389 this->read_int_sensor_(message, &pos, this->ac_output_active_power_);
390 this->read_int_sensor_(message, &pos, this->output_load_percent_);
391 this->read_int_sensor_(message, &pos, this->bus_voltage_);
392
393 this->read_float_sensor_(message, &pos, this->battery_voltage_);
394
395 this->read_int_sensor_(message, &pos, this->battery_charging_current_);
396 this->read_int_sensor_(message, &pos, this->battery_capacity_percent_);
397 this->read_int_sensor_(message, &pos, this->inverter_heat_sink_temperature_);
398
399 this->read_float_sensor_(message, &pos, this->pv_input_current_for_battery_);
400 this->read_float_sensor_(message, &pos, this->pv_input_voltage_);
401 this->read_float_sensor_(message, &pos, this->battery_voltage_scc_);
402
403 this->read_int_sensor_(message, &pos, this->battery_discharge_current_);
404
405 std::string device_status_1 = this->read_field_(message, &pos);
406 this->publish_binary_sensor_(this->get_bit_(device_status_1, 0), this->add_sbu_priority_version_);
407 this->publish_binary_sensor_(this->get_bit_(device_status_1, 1), this->configuration_status_);
408 this->publish_binary_sensor_(this->get_bit_(device_status_1, 2), this->scc_firmware_version_);
409 this->publish_binary_sensor_(this->get_bit_(device_status_1, 3), this->load_status_);
410 this->publish_binary_sensor_(this->get_bit_(device_status_1, 4), this->battery_voltage_to_steady_while_charging_);
411 this->publish_binary_sensor_(this->get_bit_(device_status_1, 5), this->charging_status_);
412 this->publish_binary_sensor_(this->get_bit_(device_status_1, 6), this->scc_charging_status_);
413 this->publish_binary_sensor_(this->get_bit_(device_status_1, 7), this->ac_charging_status_);
414
415 esphome::optional<int> battery_voltage_offset_for_fans_on = parse_number<int32_t>(this->read_field_(message, &pos));
416 if (this->battery_voltage_offset_for_fans_on_) {
417 this->battery_voltage_offset_for_fans_on_->publish_state(battery_voltage_offset_for_fans_on.value_or(NAN) / 10.0f);
418 }
419 this->read_int_sensor_(message, &pos, this->eeprom_version_);
420 this->read_int_sensor_(message, &pos, this->pv_charging_power_);
421
422 std::string device_status_2 = this->read_field_(message, &pos);
423 this->publish_binary_sensor_(this->get_bit_(device_status_2, 0), this->charging_to_floating_mode_);
424 this->publish_binary_sensor_(this->get_bit_(device_status_2, 1), this->switch_on_);
425 this->publish_binary_sensor_(this->get_bit_(device_status_2, 2), this->dustproof_installed_);
426}
427
429 std::string mode;
430 char device_mode = char(message[1]);
431 if (this->last_qmod_) {
432 this->last_qmod_->publish_state(message);
433 }
434 if (this->device_mode_) {
435 mode = device_mode;
436 this->device_mode_->publish_state(mode);
437 }
438}
439
441 // result like:"(EbkuvxzDajy"
442 // get through all char: ignore first "(" Enable flag on 'E', Disable on 'D') else set the corresponding value
443 if (this->last_qflag_) {
444 this->last_qflag_->publish_state(message);
445 }
446
447 QFLAGValues values = QFLAGValues();
448 bool enabled = true;
449 for (size_t i = 1; i < strlen(message); i++) {
450 switch (message[i]) {
451 case 'E':
452 enabled = true;
453 break;
454 case 'D':
455 enabled = false;
456 break;
457 case 'a':
458 values.silence_buzzer_open_buzzer = enabled;
459 break;
460 case 'b':
461 values.overload_bypass_function = enabled;
462 break;
463 case 'k':
464 values.lcd_escape_to_default = enabled;
465 break;
466 case 'u':
467 values.overload_restart_function = enabled;
468 break;
469 case 'v':
470 values.over_temperature_restart_function = enabled;
471 break;
472 case 'x':
473 values.backlight_on = enabled;
474 break;
475 case 'y':
477 break;
478 case 'z':
479 values.fault_code_record = enabled;
480 break;
481 case 'j':
482 values.power_saving = enabled;
483 break;
484 }
485 }
486
487 this->publish_binary_sensor_(values.silence_buzzer_open_buzzer, this->silence_buzzer_open_buzzer_);
488 this->publish_binary_sensor_(values.overload_bypass_function, this->overload_bypass_function_);
489 this->publish_binary_sensor_(values.lcd_escape_to_default, this->lcd_escape_to_default_);
490 this->publish_binary_sensor_(values.overload_restart_function, this->overload_restart_function_);
491 this->publish_binary_sensor_(values.over_temperature_restart_function, this->over_temperature_restart_function_);
492 this->publish_binary_sensor_(values.backlight_on, this->backlight_on_);
494 this->alarm_on_when_primary_source_interrupt_);
495 this->publish_binary_sensor_(values.fault_code_record, this->fault_code_record_);
496 this->publish_binary_sensor_(values.power_saving, this->power_saving_);
497}
498
500 // '(00000000000000000000000000000000'
501 // iterate over all available flag (as not all models have all flags, but at least in the same order)
502 if (this->last_qpiws_) {
503 this->last_qpiws_->publish_state(message);
504 }
505
506 size_t pos = 0;
507 this->skip_start_(message, &pos);
508 std::string flags = this->read_field_(message, &pos);
509
511 bool value_warnings_present = false;
512 bool value_faults_present = false;
513
514 for (size_t i = 0; i < 36; i++) {
515 if (i == 31 || i == 32) {
516 // special case for fault code
517 continue;
518 }
519 enabled = this->get_bit_(flags, i);
520 switch (i) {
521 case 0:
522 this->publish_binary_sensor_(enabled, this->warning_power_loss_);
523 value_warnings_present |= enabled.value_or(false);
524 break;
525 case 1:
526 this->publish_binary_sensor_(enabled, this->fault_inverter_fault_);
527 value_faults_present |= enabled.value_or(false);
528 break;
529 case 2:
530 this->publish_binary_sensor_(enabled, this->fault_bus_over_);
531 value_faults_present |= enabled.value_or(false);
532 break;
533 case 3:
534 this->publish_binary_sensor_(enabled, this->fault_bus_under_);
535 value_faults_present |= enabled.value_or(false);
536 break;
537 case 4:
538 this->publish_binary_sensor_(enabled, this->fault_bus_soft_fail_);
539 value_faults_present |= enabled.value_or(false);
540 break;
541 case 5:
542 this->publish_binary_sensor_(enabled, this->warning_line_fail_);
543 value_warnings_present |= enabled.value_or(false);
544 break;
545 case 6:
546 this->publish_binary_sensor_(enabled, this->fault_opvshort_);
547 value_faults_present |= enabled.value_or(false);
548 break;
549 case 7:
550 this->publish_binary_sensor_(enabled, this->fault_inverter_voltage_too_low_);
551 value_faults_present |= enabled.value_or(false);
552 break;
553 case 8:
554 this->publish_binary_sensor_(enabled, this->fault_inverter_voltage_too_high_);
555 value_faults_present |= enabled.value_or(false);
556 break;
557 case 9:
558 this->publish_binary_sensor_(enabled, this->warning_over_temperature_);
559 value_warnings_present |= enabled.value_or(false);
560 break;
561 case 10:
562 this->publish_binary_sensor_(enabled, this->warning_fan_lock_);
563 value_warnings_present |= enabled.value_or(false);
564 break;
565 case 11:
566 this->publish_binary_sensor_(enabled, this->warning_battery_voltage_high_);
567 value_warnings_present |= enabled.value_or(false);
568 break;
569 case 12:
570 this->publish_binary_sensor_(enabled, this->warning_battery_low_alarm_);
571 value_warnings_present |= enabled.value_or(false);
572 break;
573 case 14:
574 this->publish_binary_sensor_(enabled, this->warning_battery_under_shutdown_);
575 value_warnings_present |= enabled.value_or(false);
576 break;
577 case 15:
578 this->publish_binary_sensor_(enabled, this->warning_battery_derating_);
579 value_warnings_present |= enabled.value_or(false);
580 break;
581 case 16:
582 this->publish_binary_sensor_(enabled, this->warning_over_load_);
583 value_warnings_present |= enabled.value_or(false);
584 break;
585 case 17:
586 this->publish_binary_sensor_(enabled, this->warning_eeprom_failed_);
587 value_warnings_present |= enabled.value_or(false);
588 break;
589 case 18:
590 this->publish_binary_sensor_(enabled, this->fault_inverter_over_current_);
591 value_faults_present |= enabled.value_or(false);
592 break;
593 case 19:
594 this->publish_binary_sensor_(enabled, this->fault_inverter_soft_failed_);
595 value_faults_present |= enabled.value_or(false);
596 break;
597 case 20:
598 this->publish_binary_sensor_(enabled, this->fault_self_test_failed_);
599 value_faults_present |= enabled.value_or(false);
600 break;
601 case 21:
602 this->publish_binary_sensor_(enabled, this->fault_op_dc_voltage_over_);
603 value_faults_present |= enabled.value_or(false);
604 break;
605 case 22:
606 this->publish_binary_sensor_(enabled, this->fault_battery_open_);
607 value_faults_present |= enabled.value_or(false);
608 break;
609 case 23:
610 this->publish_binary_sensor_(enabled, this->fault_current_sensor_failed_);
611 value_faults_present |= enabled.value_or(false);
612 break;
613 case 24:
614 this->publish_binary_sensor_(enabled, this->fault_battery_short_);
615 value_faults_present |= enabled.value_or(false);
616 break;
617 case 25:
618 this->publish_binary_sensor_(enabled, this->warning_power_limit_);
619 value_warnings_present |= enabled.value_or(false);
620 break;
621 case 26:
622 this->publish_binary_sensor_(enabled, this->warning_pv_voltage_high_);
623 value_warnings_present |= enabled.value_or(false);
624 break;
625 case 27:
626 this->publish_binary_sensor_(enabled, this->fault_mppt_overload_);
627 value_faults_present |= enabled.value_or(false);
628 break;
629 case 28:
630 this->publish_binary_sensor_(enabled, this->warning_mppt_overload_);
631 value_warnings_present |= enabled.value_or(false);
632 break;
633 case 29:
634 this->publish_binary_sensor_(enabled, this->warning_battery_too_low_to_charge_);
635 value_warnings_present |= enabled.value_or(false);
636 break;
637 case 30:
638 this->publish_binary_sensor_(enabled, this->fault_dc_dc_over_current_);
639 value_faults_present |= enabled.value_or(false);
640 break;
641 case 33:
642 this->publish_binary_sensor_(enabled, this->warning_low_pv_energy_);
643 value_warnings_present |= enabled.value_or(false);
644 break;
645 case 34:
646 this->publish_binary_sensor_(enabled, this->warning_high_ac_input_during_bus_soft_start_);
647 value_warnings_present |= enabled.value_or(false);
648 case 35:
649 this->publish_binary_sensor_(enabled, this->warning_battery_equalization_);
650 value_warnings_present |= enabled.value_or(false);
651 break;
652 }
653 }
654
655 this->publish_binary_sensor_(value_warnings_present, this->warnings_present_);
656 this->publish_binary_sensor_(value_faults_present, this->faults_present_);
657
658 if (this->fault_code_) {
659 if (flags.length() < 33) {
660 this->fault_code_->publish_state(NAN);
661 } else {
662 std::string fc(flags, 31, 2);
663 this->fault_code_->publish_state(parse_number<int>(fc).value_or(NAN));
664 }
665 }
666}
667
668void Pipsolar::handle_qt_(const char *message) {
669 if (this->last_qt_) {
670 this->last_qt_->publish_state(message);
671 }
672}
673
675 if (this->last_qmn_) {
676 this->last_qmn_->publish_state(message);
677 }
678}
679
680void Pipsolar::skip_start_(const char *message, size_t *pos) {
681 if (message[*pos] == '(') {
682 (*pos)++;
683 }
684}
685void Pipsolar::skip_field_(const char *message, size_t *pos) {
686 // find delimiter or end of string
687 while (message[*pos] != '\0' && message[*pos] != ' ') {
688 (*pos)++;
689 }
690 if (message[*pos] != '\0') {
691 // skip delimiter after this field if there is one
692 (*pos)++;
693 }
694}
695std::string Pipsolar::read_field_(const char *message, size_t *pos) {
696 size_t begin = *pos;
697 // find delimiter or end of string
698 while (message[*pos] != '\0' && message[*pos] != ' ') {
699 (*pos)++;
700 }
701 if (*pos == begin) {
702 return "";
703 }
704
705 std::string field(message, begin, *pos - begin);
706
707 if (message[*pos] != '\0') {
708 // skip delimiter after this field if there is one
709 (*pos)++;
710 }
711
712 return field;
713}
714
715void Pipsolar::read_float_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor) {
716 if (sensor != nullptr) {
717 std::string field = this->read_field_(message, pos);
718 sensor->publish_state(parse_number<float>(field).value_or(NAN));
719 } else {
720 this->skip_field_(message, pos);
721 }
722}
723void Pipsolar::read_int_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor) {
724 if (sensor != nullptr) {
725 std::string field = this->read_field_(message, pos);
727 sensor->publish_state(parsed.has_value() ? parsed.value() : NAN);
728 } else {
729 this->skip_field_(message, pos);
730 }
731}
732
734 if (sensor) {
735 if (b.has_value()) {
736 sensor->publish_state(b.value());
737 } else {
738 sensor->invalidate_state();
739 }
740 }
741}
742
743esphome::optional<bool> Pipsolar::get_bit_(std::string bits, uint8_t bit_pos) {
744 if (bit_pos >= bits.length()) {
745 return {};
746 }
747 return bits[bit_pos] == '1';
748}
749
750void Pipsolar::dump_config() {
751 ESP_LOGCONFIG(TAG, "Pipsolar enabled polling commands:");
752 for (auto &enabled_polling_command : this->enabled_polling_commands_) {
753 if (enabled_polling_command.length != 0) {
754 ESP_LOGCONFIG(TAG, "%s", enabled_polling_command.command);
755 }
756 }
757}
758void Pipsolar::update() {
759 for (auto &enabled_polling_command : this->enabled_polling_commands_) {
760 if (enabled_polling_command.length != 0) {
761 enabled_polling_command.needs_update = true;
762 }
763 }
764}
765
766void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand polling_command) {
767 for (auto &enabled_polling_command : this->enabled_polling_commands_) {
768 if (enabled_polling_command.length == strlen(command)) {
769 uint8_t len = strlen(command);
770 if (memcmp(enabled_polling_command.command, command, len) == 0) {
771 return;
772 }
773 }
774 if (enabled_polling_command.length == 0) {
775 size_t length = strlen(command);
776
777 enabled_polling_command.command = new uint8_t[length + 1]; // NOLINT(cppcoreguidelines-owning-memory)
778 for (size_t i = 0; i < length + 1; i++) {
779 enabled_polling_command.command[i] = (uint8_t) command[i];
780 }
781 enabled_polling_command.errors = 0;
782 enabled_polling_command.identifier = polling_command;
783 enabled_polling_command.length = length;
784 enabled_polling_command.needs_update = true;
785 return;
786 }
787 }
788}
789
790uint16_t Pipsolar::pipsolar_crc_(uint8_t *msg, uint8_t len) {
791 uint16_t crc = crc16be(msg, len);
792 uint8_t crc_low = crc & 0xff;
793 uint8_t crc_high = crc >> 8;
794 if (crc_low == 0x28 || crc_low == 0x0d || crc_low == 0x0a)
795 crc_low++;
796 if (crc_high == 0x28 || crc_high == 0x0d || crc_high == 0x0a)
797 crc_high++;
798 crc = (crc_high << 8) | crc_low;
799 return crc;
800}
801
802} // namespace pipsolar
803} // namespace esphome
BedjetMode mode
BedJet operating mode.
Base class for all binary_sensor-type classes.
void publish_state(bool new_state)
Publish a new state to the front-end.
bool has_value() const
Definition optional.h:92
value_type value_or(U const &v) const
Definition optional.h:98
value_type const & value() const
Definition optional.h:94
static const size_t COMMAND_QUEUE_LENGTH
Definition pipsolar.h:196
void handle_qmod_(const char *message)
Definition pipsolar.cpp:428
uint8_t read_buffer_[PIPSOLAR_READ_BUFFER_LENGTH]
Definition pipsolar.h:231
uint16_t pipsolar_crc_(uint8_t *msg, uint8_t len)
Definition pipsolar.cpp:790
void handle_poll_error_(ENUMPollingCommand polling_command)
Definition pipsolar.cpp:287
void handle_qpiws_(const char *message)
Definition pipsolar.cpp:499
esphome::optional< bool > get_bit_(std::string bits, uint8_t bit_pos)
Definition pipsolar.cpp:743
void handle_qt_(const char *message)
Definition pipsolar.cpp:668
uint8_t check_incoming_length_(uint8_t length)
Definition pipsolar.cpp:164
void handle_qpigs_(const char *message)
Definition pipsolar.cpp:375
void handle_poll_response_(ENUMPollingCommand polling_command, const char *message)
Definition pipsolar.cpp:260
static const size_t PIPSOLAR_READ_BUFFER_LENGTH
Definition pipsolar.h:195
std::string read_field_(const char *message, size_t *pos)
Definition pipsolar.cpp:695
std::string command_queue_[COMMAND_QUEUE_LENGTH]
Definition pipsolar.h:229
void read_int_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor)
Definition pipsolar.cpp:723
void read_float_sensor_(const char *message, size_t *pos, sensor::Sensor *sensor)
Definition pipsolar.cpp:715
void handle_qflag_(const char *message)
Definition pipsolar.cpp:440
void skip_field_(const char *message, size_t *pos)
Definition pipsolar.cpp:685
void handle_qpiri_(const char *message)
Definition pipsolar.cpp:292
PollingCommand enabled_polling_commands_[POLLING_COMMANDS_MAX]
Definition pipsolar.h:246
void publish_binary_sensor_(esphome::optional< bool > b, binary_sensor::BinarySensor *sensor)
Definition pipsolar.cpp:733
static const size_t POLLING_COMMANDS_MAX
Definition pipsolar.h:198
void skip_start_(const char *message, size_t *pos)
Definition pipsolar.cpp:680
static const size_t COMMAND_TIMEOUT
Definition pipsolar.h:197
void add_polling_command_(const char *command, ENUMPollingCommand polling_command)
Definition pipsolar.cpp:766
void handle_qmn_(const char *message)
Definition pipsolar.cpp:674
Base-class for all sensors.
Definition sensor.h:47
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:38
void write_str(const char *str)
Definition uart.h:32
void write_array(const uint8_t *data, size_t len)
Definition uart.h:26
size_t write(uint8_t data)
Definition uart.h:57
const char * message
Definition component.cpp:38
uint16_t flags
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
Definition helpers.cpp:73
std::string size_t len
Definition helpers.h:817
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:910
size_t size_t pos
Definition helpers.h:854
uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t poly, bool refin, bool refout)
Definition helpers.cpp:113
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:25
esphome::optional< bool > overload_bypass_function
Definition pipsolar.h:34
esphome::optional< bool > alarm_on_when_primary_source_interrupt
Definition pipsolar.h:39
esphome::optional< bool > backlight_on
Definition pipsolar.h:38
esphome::optional< bool > lcd_escape_to_default
Definition pipsolar.h:35
esphome::optional< bool > fault_code_record
Definition pipsolar.h:40
esphome::optional< bool > over_temperature_restart_function
Definition pipsolar.h:37
esphome::optional< bool > silence_buzzer_open_buzzer
Definition pipsolar.h:33
esphome::optional< bool > overload_restart_function
Definition pipsolar.h:36
esphome::optional< bool > power_saving
Definition pipsolar.h:41
uint16_t length
Definition tt21100.cpp:0