Instrument Neutral Distributed Interface INDI 2.2.5
indipowerinterface.cpp
Go to the documentation of this file.
1/*
2 Power Interface
3 Copyright (C) 2025 Jasem Mutlaq ([email protected])
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19*/
20
21#include "indipowerinterface.h"
22#include "defaultdevice.h"
23#include "basedevice.h"
24
25#include <cstring>
26#include <chrono>
27#include <thread>
28
29namespace INDI
30{
31
32PowerInterface::PowerInterface(DefaultDevice *defaultDevice) : m_defaultDevice(defaultDevice)
33{
34 // Initialize Power Sensors
35 PowerSensorsNP[SENSOR_VOLTAGE].fill("SENSOR_VOLTAGE", "Voltage (V)", "%.2f", 0, 999, 0, 0);
36 PowerSensorsNP[SENSOR_CURRENT].fill("SENSOR_CURRENT", "Current (A)", "%.2f", 0, 999, 0, 0);
37 PowerSensorsNP[SENSOR_POWER].fill("SENSOR_POWER", "Power (W)", "%.2f", 0, 999, 0, 0);
38
39 // Initialize Over Voltage Protection
40 OverVoltageProtectionNP[0].fill("OVERVOLTAGE", "Max Voltage", "%.1f", 0, 999, 0, 13.8);
41
42 // Initialize Power Off on Disconnect
43 PowerOffOnDisconnectSP[0].fill("INDI_ENABLED", "Enabled", ISS_OFF);
44 PowerOffOnDisconnectSP[1].fill("INDI_DISABLED", "Disabled", ISS_ON);
45
46 // Initialize LED Control
47 LEDControlSP[0].fill("INDI_ENABLED", "On", ISS_ON);
48 LEDControlSP[1].fill("INDI_DISABLED", "Off", ISS_OFF);
49
50
51 // Initialize Power Cycle All
52 PowerCycleAllSP[0].fill("POWER_CYCLE_Toggle", "Toggle", ISS_OFF);
53}
54
55void PowerInterface::initProperties(const char *groupName, size_t nPowerPorts, size_t nDewPorts, size_t nVariablePorts,
56 size_t nAutoDewPorts, size_t nUSBPorts)
57{
58 // Main Control - Overall Power Sensors
59 PowerSensorsNP.fill(m_defaultDevice->getDeviceName(), "POWER_SENSORS", "Sensors", groupName, IP_RO, 60, IPS_IDLE);
60
61 // Over Voltage Protection
62 OverVoltageProtectionNP.fill(m_defaultDevice->getDeviceName(), "OVER_VOLTAGE_PROTECTION", "Over Voltage", groupName,
63 IP_RW, 60, IPS_IDLE);
64
65 // Power Off on Disconnect
66 PowerOffOnDisconnectSP.fill(m_defaultDevice->getDeviceName(), "POWER_OFF_DISCONNECT", "Power Off", groupName,
68
69 // LED Control
70 if (HasLEDToggle())
71 LEDControlSP.fill(m_defaultDevice->getDeviceName(), "LED_CONTROL", "LEDs", groupName, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
72
73
74 // Power Cycle All
76 PowerCycleAllSP.fill(m_defaultDevice->getDeviceName(), "POWER_CYCLE", "Cycle Power", groupName, IP_RW, ISR_ATMOST1, 60,
77 IPS_IDLE);
78
79
80 if (nPowerPorts > 0)
81 {
82 // Power Channel Label
83 PowerChannelLabelsTP.resize(nPowerPorts);
84 for (size_t i = 0; i < nPowerPorts; i++)
85 {
86 char channelNum[8];
87 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
88
89 char propName[MAXINDINAME];
90 char propLabel[MAXINDILABEL];
91 snprintf(propName, MAXINDINAME, "POWER_CHANNEL_%d", static_cast<int>(i + 1));
92 snprintf(propLabel, MAXINDILABEL, "Channel %d", static_cast<int>(i + 1));
93 PowerChannelLabelsTP[i].fill(propName, propLabel, propLabel);
94 }
96 IP_RW, 60, IPS_IDLE);
98
99 // Initialize Power Channels (12V DC)
100 PowerChannelsSP.resize(nPowerPorts);
101 for (size_t i = 0; i < nPowerPorts; i++)
102 {
103 char channelNum[8];
104 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
105
106 char propName[MAXINDINAME];
107 char propLabel[MAXINDILABEL];
108 snprintf(propName, MAXINDINAME, "POWER_CHANNEL_%d", static_cast<int>(i + 1));
109 snprintf(propLabel, MAXINDILABEL, "%s", PowerChannelLabelsTP[i].getText());
110 PowerChannelsSP[i].fill(propName, propLabel, ISS_OFF);
111 }
112 PowerChannelsSP.fill(m_defaultDevice->getDeviceName(), "POWER_CHANNELS", "Toggle DC", POWER_TAB, IP_RW, ISR_NOFMANY,
113 60, IPS_IDLE);
114
115 // Power Channel Current (only if per-channel current monitoring is available)
116
117 PowerChannelCurrentNP.resize(nPowerPorts);
118 for (size_t i = 0; i < nPowerPorts; i++)
119 {
120 char channelNum[8];
121 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
122
123 char propName[MAXINDINAME];
124 char propLabel[MAXINDILABEL];
125 snprintf(propName, MAXINDINAME, "POWER_CHANNEL_%d", static_cast<int>(i + 1));
126 snprintf(propLabel, MAXINDILABEL, "%s (A)", PowerChannelLabelsTP[i].getText());
127 PowerChannelCurrentNP[i].fill(propName, propLabel, "%.2f", 0, 999, 0, 0);
128 }
129 PowerChannelCurrentNP.fill(m_defaultDevice->getDeviceName(), "POWER_CURRENTS", "Currents", POWER_TAB, IP_RO, 60,
130 IPS_IDLE);
131 }
132 else
133 {
137 }
138
139 if (nDewPorts > 0)
140 {
141 // DEW Channel Label
142 DewChannelLabelsTP.resize(nDewPorts);
143 for (size_t i = 0; i < nDewPorts; i++)
144 {
145 char channelNum[8];
146 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
147
148 char propName[MAXINDINAME];
149 char propLabel[MAXINDILABEL];
150 snprintf(propName, MAXINDINAME, "DEW_CHANNEL_%d", static_cast<int>(i + 1));
151 snprintf(propLabel, MAXINDILABEL, "Channel %d", static_cast<int>(i + 1));
152 DewChannelLabelsTP[i].fill(propName, propLabel, propLabel);
153 }
154 DewChannelLabelsTP.fill(m_defaultDevice->getDeviceName(), "DEW_LABELS", "Labels", DEW_TAB, IP_RW, 60,
155 IPS_IDLE);
157
158 // Initialize DEW/Dew Channels
159 DewChannelsSP.resize(nDewPorts);
160 for (size_t i = 0; i < nDewPorts; i++)
161 {
162 char channelNum[8];
163 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
164
165 char propName[MAXINDINAME];
166 char propLabel[MAXINDILABEL];
167 snprintf(propName, MAXINDINAME, "DEW_CHANNEL_%d", static_cast<int>(i + 1));
168 snprintf(propLabel, MAXINDILABEL, "%s", DewChannelLabelsTP[i].getText());
169 DewChannelsSP[i].fill(propName, propLabel, ISS_OFF);
170 }
171 DewChannelsSP.fill(m_defaultDevice->getDeviceName(), "DEW_CHANNELS", "Toggle Dew", DEW_TAB, IP_RW, ISR_NOFMANY, 60,
172 IPS_IDLE);
173
174 // DEW Channel Duty Cycle
175 DewChannelDutyCycleNP.resize(nDewPorts);
176 for (size_t i = 0; i < nDewPorts; i++)
177 {
178 char channelNum[8];
179 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
180
181 char propName[MAXINDINAME];
182 char propLabel[MAXINDILABEL];
183 snprintf(propName, MAXINDINAME, "DEW_CHANNEL_%d", static_cast<int>(i + 1));
184 snprintf(propLabel, MAXINDILABEL, "%s (%%)", DewChannelLabelsTP[i].getText());
185 DewChannelDutyCycleNP[i].fill(propName, propLabel, "%.0f", 0, 100, 10, 0);
186 }
187 DewChannelDutyCycleNP.fill(m_defaultDevice->getDeviceName(), "DEW_DUTY_CYCLES", "Duty Cycles", DEW_TAB, IP_RW, 60,
188 IPS_IDLE);
189 }
190 else
191 {
195 }
196
197 // Auto Dew Control
198 if (nAutoDewPorts > 0)
199 {
200 // DEW Channel Current (only if per-channel current monitoring is available)
201 DewChannelCurrentNP.resize(nDewPorts);
202 for (size_t i = 0; i < nDewPorts; i++)
203 {
204 char channelNum[8];
205 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
206
207 char propName[MAXINDINAME];
208 char propLabel[MAXINDILABEL];
209 snprintf(propName, MAXINDINAME, "DEW_CHANNEL_%d", static_cast<int>(i + 1));
210 snprintf(propLabel, MAXINDILABEL, "%s (A)", DewChannelLabelsTP[i].getText());
211 DewChannelCurrentNP[i].fill(propName, propLabel, "%.2f", 0, 999, 0, 0);
212 }
213 DewChannelCurrentNP.fill(m_defaultDevice->getDeviceName(), "DEW_CURRENTS", "Currents", DEW_TAB, IP_RO,
214 60, IPS_IDLE);
215
216 AutoDewSP.resize(nAutoDewPorts);
217 for (size_t i = 0; i < nAutoDewPorts; i++)
218 {
219 char portNum[8];
220 snprintf(portNum, sizeof(portNum), "%d", static_cast<int>(i + 1));
221
222 char propName[MAXINDINAME];
223 char propLabel[MAXINDILABEL];
224 snprintf(propName, MAXINDINAME, "DEW_CHANNEL_%d", static_cast<int>(i + 1));
225 snprintf(propLabel, MAXINDILABEL, "%s", DewChannelLabelsTP[i].getText());
226 AutoDewSP[i].fill(propName, propLabel, ISS_OFF);
227 }
228 if (nAutoDewPorts > 0)
229 {
230 AutoDewSP.fill(m_defaultDevice->getDeviceName(), "AUTO_DEW_CONTROL", "Auto Dew Control", DEW_TAB, IP_RW, ISR_NOFMANY, 60,
231 IPS_IDLE);
232 }
233 }
234 else
235 {
236 AutoDewSP.resize(0);
238 }
239
240
241 // Initialize USB Ports
242 if (nUSBPorts > 0)
243 {
244 // USB Port Labels
245 USBPortLabelsTP.resize(nUSBPorts);
246 for (size_t i = 0; i < nUSBPorts; i++)
247 {
248 char portNum[8];
249 snprintf(portNum, sizeof(portNum), "%d", static_cast<int>(i + 1));
250
251 char propName[MAXINDINAME];
252 char propLabel[MAXINDILABEL];
253 snprintf(propName, MAXINDINAME, "USB_PORT_%d", static_cast<int>(i + 1));
254 snprintf(propLabel, MAXINDILABEL, "Port %d", static_cast<int>(i + 1));
255 USBPortLabelsTP[i].fill(propName, propLabel, propLabel);
256 }
257 if (nUSBPorts > 0)
258 {
259 USBPortLabelsTP.fill(m_defaultDevice->getDeviceName(), "USB_LABELS", "Labels", USB_TAB, IP_RW, 60,
260 IPS_IDLE);
262 }
263
264 USBPortSP.resize(nUSBPorts);
265 for (size_t i = 0; i < nUSBPorts; i++)
266 {
267 char portNum[8];
268 snprintf(portNum, sizeof(portNum), "%d", static_cast<int>(i + 1));
269
270 char propName[MAXINDINAME];
271 char propLabel[MAXINDILABEL];
272 snprintf(propName, MAXINDINAME, "USB_PORT_%d", static_cast<int>(i + 1));
273 snprintf(propLabel, MAXINDILABEL, "%s", USBPortLabelsTP[i].getText());
274 USBPortSP[i].fill(propName, propLabel, ISS_OFF);
275 }
276 if (nUSBPorts > 0)
277 {
278 USBPortSP.fill(m_defaultDevice->getDeviceName(), "USB_PORTS", "Ports", USB_TAB, IP_RW, ISR_NOFMANY, 60,
279 IPS_IDLE);
280 }
281 }
282 else
283 {
284 USBPortSP.resize(0);
286 }
287
288 // Initialize Variable Voltage Channels
289 if (nVariablePorts > 0)
290 {
291 // Variable Channel Label
292 VariableChannelLabelsTP.resize(nVariablePorts);
293 for (size_t i = 0; i < nVariablePorts; i++)
294 {
295 char channelNum[8];
296 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
297
298 char propName[MAXINDINAME];
299 char propLabel[MAXINDILABEL];
300 snprintf(propName, MAXINDINAME, "VAR_CHANNEL_%d", static_cast<int>(i + 1));
301 snprintf(propLabel, MAXINDILABEL, "Channel %d", static_cast<int>(i + 1));
302 VariableChannelLabelsTP[i].fill(propName, propLabel, propLabel);
303 }
305 IP_RW, 60, IPS_IDLE);
307
308 VariableChannelsSP.resize(nVariablePorts);
309 for (size_t i = 0; i < nVariablePorts; i++)
310 {
311 char channelNum[8];
312 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
313
314 char propName[MAXINDINAME];
315 char propLabel[MAXINDILABEL];
316 snprintf(propName, MAXINDINAME, "VAR_CHANNEL_%d", static_cast<int>(i + 1));
317 snprintf(propLabel, MAXINDILABEL, "%s", VariableChannelLabelsTP[i].getText());
318 VariableChannelsSP[i].fill(propName, propLabel, ISS_OFF);
319 }
320 VariableChannelsSP.fill(m_defaultDevice->getDeviceName(), "VARIABLE_CHANNELS", "Channels", VARIABLE_TAB, IP_RW,
321 ISR_NOFMANY, 60, IPS_IDLE);
322
323 // Variable Channel Voltage
324 VariableChannelVoltsNP.resize(nVariablePorts);
325 for (size_t i = 0; i < nVariablePorts; i++)
326 {
327 char channelNum[8];
328 snprintf(channelNum, sizeof(channelNum), "%d", static_cast<int>(i + 1));
329
330 char propName[MAXINDINAME];
331 char propLabel[MAXINDILABEL];
332 snprintf(propName, MAXINDINAME, "VAR_CHANNEL_%d", static_cast<int>(i + 1));
333 snprintf(propLabel, MAXINDILABEL, "%s (V)", VariableChannelLabelsTP[i].getText());
334 VariableChannelVoltsNP[i].fill(propName, propLabel, "%.1f", 3, 12, 0.1, 5);
335 }
336 VariableChannelVoltsNP.fill(m_defaultDevice->getDeviceName(), "VARIABLE_VOLTAGES", "Voltages", VARIABLE_TAB, IP_RW,
337 60, IPS_IDLE);
338 }
339 else
340 {
344 }
345}
346
348{
350 {
351 // Define properties only if connected
358 if (HasLEDToggle())
360 if (HasAutoDew())
361 {
363 }
366
367 // Power Channels
368 if (HasDCOutput())
369 {
371 if (HasPerPortCurrent())
372 {
374 }
376 }
377
378 // DEW Channels
379 if (HasDewOutput())
380 {
383 if (HasPerPortCurrent())
384 {
386 }
388 }
389
390 // Variable Channels
391 if (HasVariableOutput())
392 {
396 }
397
398 // USB Ports
399 if (HasUSBPort())
400 {
403 }
404 }
405 else
406 {
407 // Delete properties when disconnected
414 if (HasLEDToggle())
416 if (HasAutoDew())
417 {
419 }
422
423 // Power Channels
424 if (HasDCOutput())
425 {
427 if (HasPerPortCurrent())
428 {
430 }
432 }
433
434 // DEW Channels
435 if (HasDewOutput())
436 {
439 if (HasPerPortCurrent())
442 }
443
444 // Variable Channels
445 if (HasVariableOutput())
446 {
450 }
451
452 // USB Ports
453 if (HasUSBPort())
454 {
457 }
458 }
459
460 return true;
461}
462
463bool PowerInterface::processNumber(const char *dev, const char *name, double values[], char *names[], int n)
464{
465 if (dev != nullptr && strcmp(dev, m_defaultDevice->getDeviceName()) == 0)
466 {
467 // Over Voltage Protection
469 {
470 return m_defaultDevice->updateProperty(OverVoltageProtectionNP, values, names, n, []()
471 {
472 return true;
473 }, true);
474 }
475
476 // DEW Channel Duty Cycles
478 {
479 return m_defaultDevice->updateProperty(DewChannelDutyCycleNP, values, names, n, [this, values, names, n]()
480 {
481 bool allSuccessful = true;
482 // Match elements by name: the client may send only some of them.
483 for (int j = 0; j < n; j++)
484 {
485 const int index = DewChannelDutyCycleNP.findWidgetIndexByName(names[j]);
486 if (index < 0 || static_cast<size_t>(index) >= DewChannelsSP.size())
487 continue;
488 const size_t i = static_cast<size_t>(index);
489 // If We try to update a duty cycle while the channel is OFF, then we save as-is.
490 // Otherwise, we need to set the duty cycle on the device.
491 if (DewChannelsSP[i].getState() == ISS_ON && !SetDewPort(i, true, values[j]))
492 {
493 allSuccessful = false;
494 }
495 }
496 return allSuccessful;
497 }, true);
498 }
499
500 // Variable Channel Voltage
502 {
503 return m_defaultDevice->updateProperty(VariableChannelVoltsNP, values, names, n, [this, values, names, n]()
504 {
505 bool allSuccessful = true;
506 // Match elements by name: the client may send only some of them.
507 for (int j = 0; j < n; j++)
508 {
509 const int index = VariableChannelVoltsNP.findWidgetIndexByName(names[j]);
510 if (index < 0 || static_cast<size_t>(index) >= VariableChannelsSP.size())
511 continue;
512 const size_t i = static_cast<size_t>(index);
513 // If We try to update a voltage while the channel is OFF, then we save as-is.
514 // Otherwise, we need to set the voltage on the device.
515 if (VariableChannelsSP[i].getState() == ISS_ON && !SetVariablePort(i, true, values[j]))
516 {
517 allSuccessful = false;
518 }
519 }
520 return allSuccessful;
521 }, true);
522 }
523 }
524
525 return false;
526}
527
528bool PowerInterface::SetAutoDewEnabled(size_t port, bool enabled)
529{
530 INDI_UNUSED(port);
531 INDI_UNUSED(enabled);
532 return false;
533}
534
536{
537 // Default implementation: cycle all power ports
538 bool success = true;
539 for (size_t i = 0; i < PowerChannelsSP.size(); ++i)
540 {
541 if (!SetPowerPort(i, false)) // Turn off
542 success = false;
543 }
544 // Small delay to ensure power off
545 std::this_thread::sleep_for(std::chrono::milliseconds(500));
546 for (size_t i = 0; i < PowerChannelsSP.size(); ++i)
547 {
548 if (!SetPowerPort(i, true)) // Turn on
549 success = false;
550 }
551 return success;
552}
553
554bool PowerInterface::processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
555{
556 if (dev != nullptr && strcmp(dev, m_defaultDevice->getDeviceName()) == 0)
557 {
558 // Power Off on Disconnect
560 {
561 return m_defaultDevice->updateProperty(PowerOffOnDisconnectSP, states, names, n, []()
562 {
563 return true;
564 }, true);
565 }
566
567 // LED Control
569 {
570 return m_defaultDevice->updateProperty(LEDControlSP, states, names, n, [this, names]()
571 {
572 bool enabled = LEDControlSP[0].isNameMatch(names[0]);
573 return SetLEDEnabled(enabled);
574 }, true);
575 }
576
577 // Auto Dew Control
578 if (HasAutoDew() && AutoDewSP.isNameMatch(name))
579 {
580 return m_defaultDevice->updateProperty(AutoDewSP, states, names, n, [this, states, names, n]()
581 {
582 bool allSuccessful = true;
583 // The client may send only some elements, in any order: match them by name, not position.
584 for (int j = 0; j < n; j++)
585 {
586 const int index = AutoDewSP.findWidgetIndexByName(names[j]);
587 if (index < 0)
588 continue;
589 const size_t i = static_cast<size_t>(index);
590 // Only change if state is different
591 if (AutoDewSP[i].getState() != states[j])
592 {
593 if (!SetAutoDewEnabled(i, states[j] == ISS_ON))
594 {
595 allSuccessful = false;
596 }
597 }
598 }
599 return allSuccessful;
600 }, true);
601 }
602
603 // Power Cycle All
605 {
606 PowerCycleAllSP.update(states, names, n);
607 if (CyclePower())
608 {
610 }
611 else
612 {
614 }
617 return true;
618 }
619
620 // Power Channels
622 {
623 return m_defaultDevice->updateProperty(PowerChannelsSP, states, names, n, [this, states, names, n]()
624 {
625 bool allSuccessful = true;
626 // The client may send only some elements, in any order: match them by name, not position.
627 for (int j = 0; j < n; j++)
628 {
629 const int index = PowerChannelsSP.findWidgetIndexByName(names[j]);
630 if (index < 0)
631 continue;
632 const size_t i = static_cast<size_t>(index);
633 // Only change if state is different
634 if (PowerChannelsSP[i].getState() != states[j])
635 {
636 if (!SetPowerPort(i, states[j] == ISS_ON))
637 {
638 allSuccessful = false;
639 }
640 }
641 }
642 return allSuccessful;
643 }, true);
644 }
645
646 // DEW Channels
647 if (DewChannelsSP.isNameMatch(name))
648 {
649 return m_defaultDevice->updateProperty(DewChannelsSP, states, names, n, [this, states, names, n]()
650 {
651 bool allSuccessful = true;
652 // The client may send only some elements, in any order: match them by name, not position.
653 for (int j = 0; j < n; j++)
654 {
655 const int index = DewChannelsSP.findWidgetIndexByName(names[j]);
656 if (index < 0)
657 continue;
658 const size_t i = static_cast<size_t>(index);
659 // Only change if state is different
660 if (DewChannelsSP[i].getState() != states[j])
661 {
662 if (!SetDewPort(i, states[j] == ISS_ON, DewChannelDutyCycleNP[i].getValue()))
663 {
664 allSuccessful = false;
665 }
666 }
667 }
668 return allSuccessful;
669 }, true);
670 }
671
672 // Variable Channels
674 {
675 return m_defaultDevice->updateProperty(VariableChannelsSP, states, names, n, [this, states, names, n]()
676 {
677 bool allSuccessful = true;
678 // The client may send only some elements, in any order: match them by name, not position.
679 for (int j = 0; j < n; j++)
680 {
681 const int index = VariableChannelsSP.findWidgetIndexByName(names[j]);
682 if (index < 0)
683 continue;
684 const size_t i = static_cast<size_t>(index);
685 if (VariableChannelsSP[i].getState() != states[j])
686 {
687 if (!SetVariablePort(i, states[j] == ISS_ON, VariableChannelVoltsNP[i].getValue()))
688 {
689 allSuccessful = false;
690 }
691 }
692 }
693 return allSuccessful;
694 }, true);
695 }
696
697 // USB Ports
698 if (HasUSBPort() && USBPortSP.isNameMatch(name))
699 {
700 return m_defaultDevice->updateProperty(USBPortSP, states, names, n, [this, states, names, n]()
701 {
702 bool allSuccessful = true;
703 // The client may send only some elements, in any order: match them by name, not position.
704 for (int j = 0; j < n; j++)
705 {
706 const int index = USBPortSP.findWidgetIndexByName(names[j]);
707 if (index < 0)
708 continue;
709 const size_t i = static_cast<size_t>(index);
710 if (USBPortSP[i].getState() != states[j])
711 {
712 if (!SetUSBPort(i, states[j] == ISS_ON))
713 {
714 allSuccessful = false;
715 }
716 }
717 }
718 return allSuccessful;
719 }, true);
720 }
721 }
722
723 return false;
724}
725
726bool PowerInterface::processText(const char *dev, const char *name, char *texts[], char *names[], int n)
727{
728 if (dev != nullptr && strcmp(dev, m_defaultDevice->getDeviceName()) == 0)
729 {
730 // Power Channel Labels
732 {
733 return m_defaultDevice->updateProperty(PowerChannelLabelsTP, texts, names, n, []()
734 {
735 return true;
736 }, true);
737 }
738
739 // DEW Channel Labels
741 {
742 return m_defaultDevice->updateProperty(DewChannelLabelsTP, texts, names, n, []()
743 {
744 return true;
745 }, true);
746 }
747
748 // Variable Channel Labels
750 {
751 return m_defaultDevice->updateProperty(VariableChannelLabelsTP, texts, names, n, []()
752 {
753 return true;
754 }, true);
755 }
756
757 // USB Port Labels
759 {
760 return m_defaultDevice->updateProperty(USBPortLabelsTP, texts, names, n, []()
761 {
762 return true;
763 }, true);
764 }
765 }
766
767 return false;
768}
769
770bool PowerInterface::SetPowerPort(size_t port, bool enabled)
771{
772 INDI_UNUSED(port);
773 INDI_UNUSED(enabled);
774 return false;
775}
776
777bool PowerInterface::SetDewPort(size_t port, bool enabled, double dutyCycle)
778{
779 INDI_UNUSED(port);
780 INDI_UNUSED(enabled);
781 INDI_UNUSED(dutyCycle);
782 return false;
783}
784
785bool PowerInterface::SetVariablePort(size_t port, bool enabled, double voltage)
786{
787 INDI_UNUSED(port);
788 INDI_UNUSED(enabled);
789 INDI_UNUSED(voltage);
790 return false;
791}
792
794{
795 INDI_UNUSED(enabled);
796 return false;
797}
798
799bool PowerInterface::SetUSBPort(size_t port, bool enabled)
800{
801 INDI_UNUSED(port);
802 INDI_UNUSED(enabled);
803 return false;
804}
805
807{
812 if (HasLEDToggle())
813 LEDControlSP.save(fp);
814
815 if (HasDCOutput())
816 {
819 }
820
821 if (HasAutoDew())
822 {
823 AutoDewSP.save(fp);
824 }
825
826 if (HasDewOutput())
827 {
831 }
832
833 if (HasVariableOutput())
834 {
838 }
839
840 if (HasUSBPort())
841 {
842 USBPortSP.save(fp);
844 }
845
846 return true;
847}
848
849}
bool isConnected() const
const char * getDeviceName() const
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
bool updateProperty(PropertyType &property, ValueType *values, char *names[], int n, std::function< bool()> updater, bool saveConfig=false)
Generic helper function to update an INDI property based on an external operation.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool SetUSBPort(size_t port, bool enabled)
SetUSBPort Set USB port on/off.
virtual bool CyclePower()
CyclePower Cycle power to all ports.
INDI::PropertySwitch PowerCycleAllSP
INDI::PropertySwitch DewChannelsSP
bool saveConfigItems(FILE *fp)
saveConfigItems Save power properties in config file
virtual bool SetAutoDewEnabled(size_t port, bool enabled)
SetAutoDewEnabled Enable/disable automatic dew control.
INDI::PropertySwitch AutoDewSP
virtual bool SetDewPort(size_t port, bool enabled, double dutyCycle)
SetDewPort Set DEW/Dew port duty cycle.
INDI::PropertyNumber VariableChannelVoltsNP
INDI::PropertyNumber OverVoltageProtectionNP
virtual bool SetVariablePort(size_t port, bool enabled, double voltage)
SetVariablePort Set variable voltage port.
INDI::PropertyNumber PowerSensorsNP
INDI::PropertyText VariableChannelLabelsTP
INDI::PropertyNumber PowerChannelCurrentNP
virtual bool SetPowerPort(size_t port, bool enabled)
SetPowerPort Set power port on/off.
virtual bool SetLEDEnabled(bool enabled)
SetLEDEnabled Enable/disable power LEDs.
INDI::PropertyNumber DewChannelCurrentNP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Power number properties.
void initProperties(const char *groupName, size_t nPowerPorts=0, size_t nDewPorts=0, size_t nVariablePorts=0, size_t nAutoDewPorts=0, size_t nUSBPorts=0)
Initialize Power properties. It is recommended to call this function within initProperties() of your ...
INDI::PropertyText PowerChannelLabelsTP
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process Power text properties.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Power switch properties.
INDI::PropertySwitch PowerChannelsSP
INDI::PropertySwitch USBPortSP
INDI::PropertyNumber DewChannelDutyCycleNP
INDI::PropertyText USBPortLabelsTP
bool updateProperties()
updateProperties Define or Delete Power properties based on the connection status of the base device
INDI::PropertySwitch LEDControlSP
INDI::PropertySwitch VariableChannelsSP
DefaultDevice * m_defaultDevice
PowerInterface(DefaultDevice *defaultDevice)
INDI::PropertyText DewChannelLabelsTP
INDI::PropertySwitch PowerOffOnDisconnectSP
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
void resize(size_t size)
bool isNameMatch(const char *otherName) const
int findWidgetIndexByName(const char *name) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
#define MAXINDILABEL
Definition indiapi.h:190
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_NOFMANY
Definition indiapi.h:173
@ ISR_ATMOST1
Definition indiapi.h:172
#define MAXINDINAME
Definition indiapi.h:189
#define INDI_UNUSED(x)
Definition indidevapi.h:132
Namespace to encapsulate INDI client, drivers, and mediator classes.