3
3
* Copyright (C)
4
4
* 2003 - 2008 Arnaud Quette <arnaud.quette@free.fr>
5
5
* 2005 - 2006 Peter Selinger <selinger@users.sourceforge.net>
6
- * 2020 - 2024 Jim Klimov <jimklimov+nut@gmail.com>
6
+ * 2020 - 2025 Jim Klimov <jimklimov+nut@gmail.com>
7
7
* 2024 Alejandro González <me@alegon.dev>
8
8
*
9
9
* Note: this subdriver was initially generated as a "stub" by the
32
32
#include "cps-hid.h"
33
33
#include "usb-common.h"
34
34
35
- #define CPS_HID_VERSION "CyberPower HID 0.82 "
35
+ #define CPS_HID_VERSION "CyberPower HID 0.83 "
36
36
37
37
/* Cyber Power Systems */
38
38
#define CPS_VENDORID 0x0764
54
54
* For some devices, the reported battery voltage is off by factor
55
55
* of 1.5 so we need to apply a scale factor to it to get the real
56
56
* battery voltage. By default, the factor is 1 (no scaling).
57
+ * Similarly, some firmwares do not report the exponent well, so
58
+ * frequency values are seen as e.g. "499.0" (in "0.1 Hz" units not
59
+ * explicitly stated), instead of "49.9 Hz".
57
60
*/
58
- static double battery_scale = 1 ;
59
- static int might_need_battery_scale = 0 ;
60
- static int battery_scale_checked = 0 ;
61
+ static double battery_scale = 1 , input_freq_scale = 1 , output_freq_scale = 1 ;
62
+ static int might_need_battery_scale = 0 , might_need_freq_scale = 0 ;
63
+ static int battery_scale_checked = 0 , input_freq_scale_checked = 0 , output_freq_scale_checked = 0 ;
61
64
62
65
/*! If the ratio of the battery voltage to the nominal battery voltage exceeds
63
66
* this factor, we assume that the battery voltage needs to be scaled by 2/3.
@@ -69,6 +72,8 @@ static void *cps_battery_scale(USBDevice_t *device)
69
72
NUT_UNUSED_VARIABLE (device );
70
73
71
74
might_need_battery_scale = 1 ;
75
+ might_need_freq_scale = 1 ;
76
+
72
77
return NULL ;
73
78
}
74
79
@@ -88,6 +93,153 @@ static usb_device_id_t cps_usb_device_table[] = {
88
93
{ 0 , 0 , NULL }
89
94
};
90
95
96
+ /*! Adjusts frequency if it is order(s) of magnitude off
97
+ * is_input = 1 for input.frequency and 0 for output.frequency
98
+ */
99
+ static void cps_adjust_frequency_scale (double freq_report , int is_input )
100
+ {
101
+ const char * freq_low_str , * freq_high_str , * freq_nom_str ;
102
+ double freq_low = 0 , freq_high = 0 , freq_nom = 0 ;
103
+
104
+ if ((is_input && input_freq_scale_checked ) || (!is_input && output_freq_scale_checked ))
105
+ return ;
106
+
107
+ /* May be not available from device itself; but still
108
+ * may be set by user as default/override options;
109
+ * if not, we default for 50Hz and/or 60Hz range +- 10%.
110
+ */
111
+ freq_nom_str = dstate_getinfo (is_input ? "input.frequency.nominal" : "output.frequency.nominal" );
112
+ freq_low_str = dstate_getinfo (is_input ? "input.frequency.low" : "output.frequency.low" );
113
+ freq_high_str = dstate_getinfo (is_input ? "input.frequency.high" : "output.frequency.high" );
114
+
115
+ if (freq_nom_str )
116
+ freq_nom = strtod (freq_nom_str , NULL );
117
+ if (freq_low_str )
118
+ freq_low = strtod (freq_low_str , NULL );
119
+ if (freq_high_str )
120
+ freq_high = strtod (freq_high_str , NULL );
121
+
122
+ if (d_equal (freq_nom , 0 )) {
123
+ if (45 < freq_low && freq_low <= 50 )
124
+ freq_nom = 50 ;
125
+ else if (50 <= freq_high && freq_high <= 55 )
126
+ freq_nom = 50 ;
127
+ else if (45 < freq_report && freq_report <= 55 )
128
+ freq_nom = 50 ;
129
+ else if (450 < freq_report && freq_report <= 550 )
130
+ freq_nom = 50 ;
131
+ else if (55 < freq_low && freq_low <= 60 )
132
+ freq_nom = 60 ;
133
+ else if (60 <= freq_high && freq_high <= 65 )
134
+ freq_nom = 60 ;
135
+ else if (55 < freq_report && freq_report <= 65 )
136
+ freq_nom = 60 ;
137
+ else if (550 < freq_report && freq_report <= 650 )
138
+ freq_nom = 60 ;
139
+
140
+ upsdebugx (3 , "%s: '%sput.frequency.nominal' is %s, guessed %0.1f%s" ,
141
+ __func__ , is_input ? "in" : "out" , NUT_STRARG (freq_nom_str ),
142
+ d_equal (freq_nom , 0 ) ? 55 : freq_nom ,
143
+ d_equal (freq_nom , 0 ) ? " (50 or 60Hz range)" : "" );
144
+ }
145
+
146
+ if (d_equal (freq_low , 0 )) {
147
+ if (d_equal (freq_nom , 0 ))
148
+ freq_low = 45.0 ;
149
+ else
150
+ freq_low = freq_nom * 0.95 ;
151
+
152
+ upsdebugx (3 , "%s: '%sput.frequency.low' is %s, defaulting to %0.1f" ,
153
+ __func__ , is_input ? "in" : "out" , NUT_STRARG (freq_low_str ), freq_low );
154
+ }
155
+
156
+ if (d_equal (freq_high , 0 )) {
157
+ if (d_equal (freq_nom , 0 ))
158
+ freq_high = 65.0 ;
159
+ else
160
+ freq_high = freq_nom * 1.05 ;
161
+
162
+ upsdebugx (3 , "%s: '%sput.frequency.high' is %s, defaulting to %0.1f" ,
163
+ __func__ , is_input ? "in" : "out" , NUT_STRARG (freq_high_str ), freq_high );
164
+ }
165
+
166
+ if (freq_low <= freq_report && freq_report <= freq_high ) {
167
+ if (is_input ) {
168
+ input_freq_scale = 1.0 ;
169
+ input_freq_scale_checked = 1 ;
170
+ } else {
171
+ output_freq_scale = 1.0 ;
172
+ output_freq_scale_checked = 1 ;
173
+ }
174
+ /* We should be here once per freq type... */
175
+ upsdebugx (1 , "%s: Determined scaling factor "
176
+ "needed for '%sput.frequency': 1.0" ,
177
+ __func__ , is_input ? "in" : "out" );
178
+ }
179
+ else
180
+ if (freq_low <= freq_report /10.0 && freq_report /10.0 <= freq_high ) {
181
+ if (is_input ) {
182
+ input_freq_scale = 0.1 ;
183
+ input_freq_scale_checked = 1 ;
184
+ } else {
185
+ output_freq_scale = 0.1 ;
186
+ output_freq_scale_checked = 1 ;
187
+ }
188
+ /* We should be here once per freq type... */
189
+ upsdebugx (1 , "%s: Determined scaling factor "
190
+ "needed for '%sput.frequency': 0.1" ,
191
+ __func__ , is_input ? "in" : "out" );
192
+ }
193
+ else
194
+ {
195
+ /* We might return here, so do not log too loudly */
196
+ upsdebugx (2 , "%s: Could not determine scaling factor "
197
+ "needed for '%sput.frequency', will report "
198
+ "it as is (and might detect better later)" ,
199
+ __func__ , is_input ? "in" : "out" );
200
+ }
201
+ }
202
+
203
+ /* returns statically allocated string - must not use it again before
204
+ done with result! */
205
+ static const char * cps_input_freq_fun (double value )
206
+ {
207
+ static char buf [8 ];
208
+
209
+ if (might_need_freq_scale ) {
210
+ cps_adjust_frequency_scale (value , 1 );
211
+ }
212
+
213
+ upsdebugx (5 , "%s: input_freq_scale = %.3f" , __func__ , input_freq_scale );
214
+ snprintf (buf , sizeof (buf ), "%.1f" , input_freq_scale * value );
215
+
216
+ return buf ;
217
+ }
218
+
219
+ static info_lkp_t cps_input_freq [] = {
220
+ { 0 , NULL , & cps_input_freq_fun , NULL }
221
+ };
222
+
223
+ /* returns statically allocated string - must not use it again before
224
+ done with result! */
225
+ static const char * cps_output_freq_fun (double value )
226
+ {
227
+ static char buf [8 ];
228
+
229
+ if (might_need_freq_scale ) {
230
+ cps_adjust_frequency_scale (value , 0 );
231
+ }
232
+
233
+ upsdebugx (5 , "%s: output_freq_scale = %.3f" , __func__ , output_freq_scale );
234
+ snprintf (buf , sizeof (buf ), "%.1f" , output_freq_scale * value );
235
+
236
+ return buf ;
237
+ }
238
+
239
+ static info_lkp_t cps_output_freq [] = {
240
+ { 0 , NULL , & cps_output_freq_fun , NULL }
241
+ };
242
+
91
243
/*! Adjusts @a battery_scale if voltage is well above nominal.
92
244
*/
93
245
static void cps_adjust_battery_scale (double batt_volt )
@@ -247,18 +399,22 @@ static hid_info_t cps_hid2nut[] = {
247
399
{ "BOOL" , 0 , 0 , "UPS.Output.Overload" , NULL , NULL , 0 , overload_info },
248
400
249
401
/* Input page */
250
- { "input.frequency" , 0 , 0 , "UPS.Input.Frequency" , NULL , "%.1f" , 0 , NULL },
402
+ /* FIXME: Check if something like "UPS.Flow([N]?).ConfigFrequency"
403
+ * is available for "input.frequency.nominal" */
404
+ { "input.frequency" , 0 , 0 , "UPS.Input.Frequency" , NULL , "%.1f" , 0 , cps_input_freq },
251
405
{ "input.voltage.nominal" , 0 , 0 , "UPS.Input.ConfigVoltage" , NULL , "%.0f" , 0 , NULL },
252
406
{ "input.voltage" , 0 , 0 , "UPS.Input.Voltage" , NULL , "%.1f" , 0 , NULL },
253
407
{ "input.transfer.low" , ST_FLAG_RW | ST_FLAG_STRING , 10 , "UPS.Input.LowVoltageTransfer" , NULL , "%.0f" , HU_FLAG_SEMI_STATIC , NULL },
254
408
{ "input.transfer.high" , ST_FLAG_RW | ST_FLAG_STRING , 10 , "UPS.Input.HighVoltageTransfer" , NULL , "%.0f" , HU_FLAG_SEMI_STATIC , NULL },
255
- /* used by CP1350EPFCLCD */
409
+ /* used by CP1350EPFCLCD; why oh why "UPS.Output"?.. */
256
410
{ "input.transfer.low" , ST_FLAG_RW | ST_FLAG_STRING , 10 , "UPS.Output.LowVoltageTransfer" , NULL , "%.0f" , HU_FLAG_SEMI_STATIC , NULL },
257
411
{ "input.transfer.high" , ST_FLAG_RW | ST_FLAG_STRING , 10 , "UPS.Output.HighVoltageTransfer" , NULL , "%.0f" , HU_FLAG_SEMI_STATIC , NULL },
258
412
{ "input.sensitivity" , ST_FLAG_RW | ST_FLAG_STRING , 0 , "UPS.Output.CPSInputSensitivity" , NULL , "%s" , HU_FLAG_SEMI_STATIC | HU_FLAG_ENUM , cps_sensitivity_info },
259
413
260
414
/* Output page */
261
- { "output.frequency" , 0 , 0 , "UPS.Output.Frequency" , NULL , "%.1f" , 0 , NULL },
415
+ /* FIXME: Check if something like "UPS.Flow([N]?).ConfigFrequency"
416
+ * is available for "output.frequency.nominal" */
417
+ { "output.frequency" , 0 , 0 , "UPS.Output.Frequency" , NULL , "%.1f" , 0 , cps_output_freq },
262
418
{ "output.voltage" , 0 , 0 , "UPS.Output.Voltage" , NULL , "%.1f" , 0 , NULL },
263
419
{ "output.voltage.nominal" , 0 , 0 , "UPS.Output.ConfigVoltage" , NULL , "%.0f" , 0 , NULL },
264
420
0 commit comments