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