pvoutput_client/types/params.rs
1//! Parameters for the various endpoints.
2
3use std::{collections::HashMap, fmt::Display};
4
5use jiff::civil::{Date, Time};
6use serde::{Serialize, Serializer};
7use serde_repr::Serialize_repr;
8
9use crate::types::Conditions;
10
11/// Parameters for the [`get_status`](crate::Client::get_status) endpoint.
12#[derive(Debug, Serialize, Clone, Default)]
13pub struct GetStatusParams {
14 /// Date to retrieve status for.
15 #[serde(rename = "d", serialize_with = "jiff_ymd")]
16 pub date: Option<Date>,
17
18 /// Time to retrieve status for.
19 #[serde(rename = "t", serialize_with = "jiff_hhmm")]
20 pub time: Option<Time>,
21
22 /// Return [history](crate::Client::get_status_history) results in ascending order.
23 #[serde(rename = "asc", serialize_with = "bool_int")]
24 pub ascending: bool,
25
26 /// Number of results to return when making a [history request](crate::Client::get_status_history), up to a maximum
27 /// of **288** (one day of data).
28 ///
29 /// Defaults to **30**.
30 pub limit: Option<u16>,
31
32 /// If set, only statuses after this time will be returned.
33 #[serde(serialize_with = "jiff_hhmm")]
34 pub from: Option<Time>,
35
36 /// If set, only statuses before this time will be returned.
37 #[serde(serialize_with = "jiff_hhmm")]
38 pub to: Option<Time>,
39
40 /// Return extended data.
41 ///
42 /// Requires [donation status](https://pvoutput.org/help/donations.html).
43 #[serde(rename = "ext", serialize_with = "bool_int")]
44 pub extended_data: bool,
45
46 /// System ID to retrieve status for.
47 /// If unset, the [`Client`](crate::Client)'s authenticated system is used.
48 ///
49 /// Requires [donation status](https://pvoutput.org/help/donations.html).
50 #[serde(rename = "sid1")]
51 pub system_id: Option<u32>,
52}
53
54#[derive(Debug, Serialize, Clone, Default)]
55pub(crate) struct GetStatusParamsOuter {
56 #[serde(flatten)]
57 pub params: GetStatusParams,
58 #[serde(serialize_with = "bool_int")]
59 pub h: bool,
60 #[serde(serialize_with = "bool_int")]
61 pub stats: bool,
62}
63
64impl GetStatusParamsOuter {
65 pub const fn new(params: GetStatusParams, h: bool, stats: bool) -> Self { Self { params, h, stats } }
66}
67
68/// Parameters for the [`get_statistics`](crate::Client::get_statistics) endpoint.
69#[derive(Debug, Serialize, Clone, Default)]
70pub struct GetStatisticsParams {
71 /// Date to measure statistics from.
72 #[serde(rename = "df")]
73 pub date_from: Option<Date>,
74
75 /// Date to measure statistics to.
76 #[serde(rename = "dt")]
77 pub date_to: Option<Date>,
78
79 /// Return consumption data.
80 ///
81 /// This information is only available to the system owner.
82 /// Requesting data for another system via the `system_id` parameter will not work.
83 #[serde(rename = "c", serialize_with = "bool_int")]
84 pub consumption: bool,
85
86 /// Return credit and debit data.
87 ///
88 /// This information is only available to the system owner.
89 /// Requesting data for another system via the `system_id` parameter will not work.
90 #[serde(rename = "crdr", serialize_with = "bool_int")]
91 pub credit_and_debit: bool,
92
93 /// System ID to retrieve statistics for.
94 /// If unset, the [`Client`](crate::Client)'s authenticated system is used.
95 ///
96 /// Requires [donation status](https://pvoutput.org/help/donations.html).
97 #[serde(rename = "sid1")]
98 pub system_id: Option<u32>,
99}
100
101/// Parameters for the [`get_system`](crate::Client::get_system) endpoint.
102#[derive(Debug, Serialize, Clone, Default)]
103#[allow(clippy::struct_excessive_bools)]
104pub struct GetSystemParams {
105 /// Return information about the system's secondary array if one is present.
106 #[serde(rename = "array2", serialize_with = "bool_int")]
107 pub secondary_array: bool,
108
109 /// Return information about the system's tariffs.
110 #[serde(serialize_with = "bool_int")]
111 pub tariffs: bool,
112
113 /// Return a list of the system's teams.
114 #[serde(serialize_with = "bool_int")]
115 pub teams: bool,
116
117 /// Return monthly estimates for the system.
118 #[serde(rename = "est", serialize_with = "bool_int")]
119 pub monthly_estimates: bool,
120
121 /// Return the system's donation count.
122 #[serde(serialize_with = "bool_int")]
123 pub donations: bool,
124
125 /// System ID to retrieve information for.
126 /// If unset, the [`Client`](crate::Client)'s authenticated system is used.
127 ///
128 /// Requires [donation status](https://pvoutput.org/help/donations.html).
129 pub system_id: Option<u32>,
130
131 /// Return extended data.
132 #[serde(rename = "ext", serialize_with = "bool_int")]
133 pub extended_data: bool,
134}
135
136impl GetSystemParams {
137 /// Creates a new [`GetSystemParams`] with all fields set to `true`.
138 #[must_use]
139 pub const fn all() -> Self {
140 Self {
141 secondary_array: true,
142 tariffs: true,
143 teams: true,
144 monthly_estimates: true,
145 donations: true,
146 system_id: None,
147 extended_data: true,
148 }
149 }
150
151 /// Creates a new [`GetSystemParams`] for the given system with all fields set to `true`.
152 ///
153 /// Requires [donation status](https://pvoutput.org/help/donations.html).
154 #[must_use]
155 pub const fn all_for_system(system: u32) -> Self {
156 Self {
157 system_id: Some(system),
158 ..Self::all()
159 }
160 }
161}
162
163/// Parameters for the [`get_outputs`](crate::Client::get_outputs),
164/// [`get_aggregate_outputs`](crate::Client::get_aggregate_outputs),
165/// and [`get_team_outputs`](crate::Client::get_team_outputs) endpoints.
166#[derive(Debug, Serialize, Clone, Default)]
167pub struct GetOutputParams {
168 /// Date to retrieve outputs from.
169 #[serde(rename = "df", serialize_with = "jiff_ymd")]
170 pub date_from: Option<Date>,
171
172 /// Date to retrieve outputs to.
173 #[serde(rename = "dt", serialize_with = "jiff_ymd")]
174 pub date_to: Option<Date>,
175
176 /// Maximum number of outputs to retrieve.
177 /// Limited to **50** for non-donors and **150** for [donors](https://pvoutput.org/help/donations.html).
178 pub limit: Option<u8>,
179
180 /// System ID to retrieve outputs for.
181 /// If unset, the [`Client`](crate::Client)'s authenticated system is used.
182 ///
183 /// Requires [donation status](https://pvoutput.org/help/donations.html).
184 #[serde(rename = "sid1")]
185 pub system_id: Option<u32>,
186
187 /// Retrieve insolation data.
188 /// Not supported for [team](crate::Client::get_team_outputs) or
189 /// [aggregate outputs](crate::Client::get_aggregate_outputs).
190 ///
191 /// Requires [donation status](https://pvoutput.org/help/donations.html).
192 #[serde(serialize_with = "bool_int")]
193 pub insolation: bool,
194
195 /// Retrieve time of export data.
196 /// Not supported for [team outputs](crate::Client::get_team_outputs).
197 ///
198 /// Requires [donation status](https://pvoutput.org/help/donations.html).
199 #[serde(rename = "timeofexport", serialize_with = "bool_int")]
200 pub time_of_export: bool,
201}
202
203/// Aggregation level for [`get_aggregate_outputs`](crate::Client::get_aggregate_outputs).
204#[derive(Copy, Clone, Debug, Serialize, PartialEq, Eq, Hash)]
205pub enum Aggregation {
206 #[serde(rename = "m")]
207 Monthly,
208
209 #[serde(rename = "y")]
210 Yearly,
211}
212
213#[derive(Debug, Serialize, Clone, Default)]
214pub(crate) struct GetOutputParamsOuter {
215 #[serde(flatten)]
216 pub params: GetOutputParams,
217
218 pub a: Option<Aggregation>,
219 pub tid: Option<u32>,
220}
221
222/// Parameters for the [`get_extended`](crate::Client::get_extended) endpoint.
223#[derive(Debug, Serialize, Clone, Default)]
224pub struct GetExtendedParams {
225 /// Start of the date range to retrieve extended data for.
226 #[serde(rename = "df")]
227 pub date_from: Option<Date>,
228
229 /// End of the date range to retrieve extended data for.
230 #[serde(rename = "dt")]
231 pub date_to: Option<Date>,
232
233 /// Maximum number of outputs to retrieve. Limited to **fifty**.
234 pub limit: Option<u8>,
235}
236
237/// Parameters for the [`get_insolation`](crate::Client::get_insolation) endpoint.
238#[derive(Debug, Serialize, Clone, Default)]
239pub struct GetInsolationParams {
240 /// Date to retrieve insolation data for.
241 ///
242 /// If not set, today's date will be used.
243 #[serde(rename = "d", serialize_with = "jiff_ymd")]
244 pub date: Option<Date>,
245
246 /// Timezone to use for insolation calculation.
247 ///
248 /// If not set, the system's timezone will be used.
249 /// If the system doesn't have a timezone, UTC will be used as a fallback.
250 #[serde(rename = "tz")]
251 pub timezone: Option<String>,
252
253 /// Latitude and longitude to use for insolation calculation.
254 ///
255 /// If not set, the system's latitude and longitude will be used.
256 /// If the system doesn't have location information, the system's postcode's latitude and longitude will be used.
257 #[serde(rename = "ll", serialize_with = "lat_long")]
258 pub location: Option<(f32, f32)>,
259
260 /// System to retrieve insolation data for.
261 ///
262 /// If unset, the [`Client`](crate::Client)'s authenticated system is used.
263 #[serde(rename = "sid1")]
264 pub system_id: Option<u32>,
265}
266
267/// Parameters for the [`search`](crate::Client::search) endpoint.
268#[derive(Debug, Serialize, Clone)]
269pub struct SearchParams {
270 /// Search query.
271 ///
272 /// See the [PVOutput docs](https://pvoutput.org/help/searching.html#searching) for further information.
273 #[serde(rename = "q")]
274 pub query: String,
275
276 /// Latitude and longitude to use as the point of origin for
277 /// [distance searches](https://pvoutput.org/help/searching.html#:~:text=Distance%20(System)).
278 #[serde(rename = "ll", serialize_with = "lat_long")]
279 pub location: Option<(f32, f32)>,
280
281 /// Return country names alongside postcodes.
282 #[serde(rename = "country", serialize_with = "bool_int")]
283 pub country_names: bool,
284
285 /// Filter results by country code.
286 ///
287 /// Required for postcode distance searches (e.g. `q=4000 20km`)
288 pub country_code: Option<CountryCode>,
289
290 /// Only return systems that were last seen this number of days ago or fewer.
291 pub seen: Option<u32>,
292}
293
294impl SearchParams {
295 /// Create a new search query.
296 ///
297 /// `location`, `country_code`, and `seen` will be set to `None`, and `country_names` will be set to `false`.
298 pub fn new(query: impl Into<String>) -> Self {
299 Self {
300 query: query.into(),
301 location: None,
302 country_names: false,
303 country_code: None,
304 seen: None,
305 }
306 }
307}
308
309/// Country codes for use with [`SearchParams`].
310// documented here: https://pvoutput.org/help/api_specification.html#country-codes
311#[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
312pub enum CountryCode {
313 /// Australia.
314 #[serde(rename = "au")]
315 Australia,
316
317 /// Belgium (Belgiƫ).
318 #[serde(rename = "be")]
319 Belgium,
320
321 /// Denmark (Danmark).
322 #[serde(rename = "dk")]
323 Denmark,
324
325 /// Italy (Italia).
326 #[serde(rename = "it")]
327 Italy,
328
329 /// The Netherlands (Nederland).
330 #[serde(rename = "nl")]
331 Netherlands,
332
333 /// The United Kingdom of Great Britain and Northern Ireland.
334 #[serde(rename = "uk")]
335 UnitedKingdom,
336
337 /// The United States of America.
338 #[serde(rename = "us")]
339 UnitedStates,
340}
341
342/// Parameters for the [`add_outputs`](crate::Client::add_outputs) endpoint.
343///
344/// # Example
345/// [`AddOutputParams::new`] can be used to create a parameter struct with all other fields set to `None`.
346/// This can be used to easily create parameters with only one or two fields set:
347/// ```rust
348/// use pvoutput_client::types::{Conditions, params::AddOutputParams};
349/// use jiff::civil::Date;
350///
351/// let params = AddOutputParams {
352/// generated: Some(1_500),
353/// conditions: Some(Conditions::Cloudy),
354/// ..AddOutputParams::new(Date::constant(2025, 09, 22))
355/// };
356/// ```
357#[derive(Serialize, Clone, Debug)]
358pub struct AddOutputParams {
359 /// Output date.
360 ///
361 /// Must be after 2000-01-01 and not in the future.
362 #[serde(serialize_with = "jiff_ymd_direct")]
363 pub date: Date,
364
365 /// Power generated in watt-hours.
366 /// This is *required* if an output does not yet exist for this date.
367 pub generated: Option<u32>,
368
369 /// Power exported in watt-hours.
370 pub exported: Option<u32>,
371
372 /// Peak power output in watts.
373 pub peak: Option<u32>,
374
375 /// The time at which the peak output was recorded.
376 /// Seconds will be truncated -- `11:15:05` becomes `11:15`.
377 #[serde(serialize_with = "jiff_hhmm")]
378 pub peak_time: Option<Time>,
379
380 /// The day's weather conditions.
381 pub conditions: Option<Conditions>,
382
383 /// The minimum recorded temperature in degrees Celsius.
384 ///
385 /// If set, a maximum temperature must also be provided, and the minimum temperature must be lower than the maximum
386 /// temperature.
387 ///
388 /// Both temperatures must be between -100.0 and 100.0.
389 pub min_temp: Option<f32>,
390
391 /// The maximum recorded temperature in degrees Celsius.
392 ///
393 /// If set, a minimum temperature must also be provided, and the minimum temperature must be lower than the maximum
394 /// temperature.
395 ///
396 /// Both temperatures must be between -100.0 and 100.0.
397 pub max_temp: Option<f32>,
398
399 /// A comment.
400 // TODO: handle semicolons and newlines in comments
401 pub comment: Option<String>,
402
403 /// The amount of energy imported during the peak tariff in watt-hours.
404 pub import_peak: Option<u32>,
405
406 /// The amount of energy imported during the off-peak tariff in watt-hours.
407 pub import_off_peak: Option<u32>,
408
409 /// The amount of energy imported during the shoulder tariff in watt-hours.
410 pub import_shoulder: Option<u32>,
411
412 /// The amount of energy imported during the high-shoulder tariff in watt-hours.
413 pub import_high_shoulder: Option<u32>,
414
415 /// Energy consumption in watt-hours.
416 ///
417 /// Limited to 999,999,999 Wh.
418 pub consumption: Option<u32>,
419
420 /// The amount of energy exported during the peak tariff in watt-hours.
421 pub export_peak: Option<u32>,
422
423 /// The amount of energy exported during the off-peak tariff in watt-hours.
424 pub export_off_peak: Option<u32>,
425
426 /// The amount of energy exported during the shoulder tariff in watt-hours.
427 pub export_shoulder: Option<u32>,
428
429 /// The amount of energy exported during the high-shoulder tariff in watt-hours.
430 pub export_high_shoulder: Option<u32>,
431}
432
433impl AddOutputParams {
434 /// Creates a new [`AddOutputParams`] for the given date with all other fields set to `None`.
435 #[must_use]
436 pub const fn new(date: Date) -> Self {
437 Self {
438 date,
439 generated: None,
440 exported: None,
441 peak: None,
442 peak_time: None,
443 conditions: None,
444 min_temp: None,
445 max_temp: None,
446 comment: None,
447 import_peak: None,
448 import_off_peak: None,
449 import_shoulder: None,
450 import_high_shoulder: None,
451 consumption: None,
452 export_peak: None,
453 export_off_peak: None,
454 export_shoulder: None,
455 export_high_shoulder: None,
456 }
457 }
458}
459
460/// Common parameters for the [`add_status`](crate::Client::add_status),
461/// [`add_batch_status`](crate::Client::add_batch_status), and
462/// [`add_batch_status_net`](crate::Client::add_batch_status_net) endpoints.
463#[allow(clippy::too_long_first_doc_paragraph)]
464#[derive(Clone, Debug, Serialize)]
465pub struct AddStatusCommon {
466 /// The date for this status.
467 ///
468 /// Must be within the past **fourteen days**, or the past **ninety days** for
469 /// accounts with [donation status](https://pvoutput.org/help/donations.html).
470 #[serde(rename = "d", serialize_with = "jiff_ymd_direct")]
471 pub date: Date,
472
473 /// The time for this status.
474 ///
475 /// Times will be rounded to the nearest configured
476 /// [status interval](https://pvoutput.org/help/live_data.html#live-configuration-status-interval).
477 #[serde(rename = "t", serialize_with = "jiff_hhmm_direct")]
478 pub time: Time,
479
480 /// Energy generation in watt-hours.
481 ///
482 /// This field is **required** when using the batch status endpoint.
483 #[serde(rename = "v1")]
484 pub energy_generation: Option<u32>,
485
486 /// Power generation in watts.
487 #[serde(rename = "v2")]
488 pub power_generation: Option<i32>,
489
490 /// Energy consumption in watt-hours.
491 ///
492 /// Limited to **200,000 Wh** for free accounts, or **999,999,999 Wh** for
493 /// accounts with [donation status](https://pvoutput.org/help/donations.html).
494 #[serde(rename = "v3")]
495 pub energy_consumption: Option<u32>,
496
497 /// Power consumption in watts.
498 ///
499 /// Limited to **100,000 W** for free accounts, or **2,000,000 W** for
500 /// accounts with [donation status](https://pvoutput.org/help/donations.html).
501 #[serde(rename = "v4")]
502 pub power_consumption: Option<i32>,
503
504 /// Temperature in degrees Celsius.
505 #[serde(rename = "v5")]
506 pub temperature: Option<f32>,
507
508 /// Voltage in volts.
509 #[serde(rename = "v6")]
510 pub voltage: Option<f32>,
511}
512
513impl AddStatusCommon {
514 /// Creates a new [`AddStatusCommon`] for the given date and time with all other fields set to `None`.
515 #[must_use]
516 pub const fn new(date: Date, time: Time) -> Self {
517 Self {
518 date,
519 time,
520 energy_generation: None,
521 power_generation: None,
522 energy_consumption: None,
523 power_consumption: None,
524 temperature: None,
525 voltage: None,
526 }
527 }
528}
529
530/// Extended parameters for use with the [`AddStatusParams`] and [`AddBatchStatusParams`] structs.
531#[derive(Clone, Debug, Serialize, Default)]
532pub struct AddExtendedParams {
533 /// User-defined [extended data](https://pvoutput.org/help/extended_data.html) parameter 1.
534 #[serde(rename = "v7")]
535 pub extended_value_1: Option<f32>,
536
537 /// User-defined [extended data](https://pvoutput.org/help/extended_data.html) parameter 2.
538 #[serde(rename = "v8")]
539 pub extended_value_2: Option<f32>,
540
541 /// User-defined [extended data](https://pvoutput.org/help/extended_data.html) parameter 3.
542 #[serde(rename = "v9")]
543 pub extended_value_3: Option<f32>,
544
545 /// User-defined [extended data](https://pvoutput.org/help/extended_data.html) parameter 4.
546 #[serde(rename = "v10")]
547 pub extended_value_4: Option<f32>,
548
549 /// User-defined [extended data](https://pvoutput.org/help/extended_data.html) parameter 5.
550 #[serde(rename = "v11")]
551 pub extended_value_5: Option<f32>,
552
553 /// User-defined [extended data](https://pvoutput.org/help/extended_data.html) parameter 6.
554 #[serde(rename = "v12")]
555 pub extended_value_6: Option<f32>,
556}
557
558/// Parameters for the [`add_status`](crate::Client::add_status) endpoint.
559#[derive(Clone, Debug, Serialize)]
560pub struct AddStatusParams {
561 /// Parameters common to the [`add_status`](crate::Client::add_status) and
562 /// [`add_batch_status`](crate::Client::add_batch_status) endpoints.
563 #[serde(flatten)]
564 pub common: AddStatusCommon,
565
566 /// Whether to process this status as a cumulative status, and if so, which fields are cumulative.
567 #[serde(rename = "c1")]
568 pub cumulative_flag: CumulativeFlag,
569
570 /// Whether to process this status as net data.
571 ///
572 /// See the [PVOutput docs](https://pvoutput.org/help/api_specification.html#net-data) for further information.
573 #[serde(rename = "n", serialize_with = "bool_int")]
574 pub net_data: bool,
575
576 /// Extended data.
577 ///
578 /// Requires [donation status](https://pvoutput.org/help/donations.html).
579 #[serde(flatten)]
580 pub extended_data: Option<AddExtendedParams>,
581
582 /// A comment.
583 ///
584 /// Limited to a maximum of thirty characters.
585 ///
586 /// Requires [donation status](https://pvoutput.org/help/donations.html).
587 #[serde(rename = "m1")]
588 pub message: Option<String>,
589
590 /// Battery information.
591 ///
592 /// Requires [donation status](https://pvoutput.org/help/donations.html).
593 #[serde(flatten)]
594 pub battery: Option<AddBatteryParams>,
595}
596
597impl AddStatusParams {
598 /// Creates a new instance of [`AddStatusParams`] with the given common parameters and all other fields set to
599 /// `None`.
600 #[must_use]
601 pub const fn new(common: AddStatusCommon) -> Self {
602 Self {
603 common,
604 cumulative_flag: CumulativeFlag::False,
605 net_data: false,
606 extended_data: None,
607 message: None,
608 battery: None,
609 }
610 }
611}
612
613impl From<AddStatusCommon> for AddStatusParams {
614 fn from(common: AddStatusCommon) -> Self { Self::new(common) }
615}
616
617#[derive(Clone, Debug, Serialize, Default)]
618pub struct AddBatteryParams {
619 /// Battery power in watts.
620 /// A positive value indicates charging either from generation or load (consumption).
621 /// A negative value indicates discharging.
622 #[serde(rename = "b1")]
623 pub power: i32,
624
625 /// State of charge as a percentage.
626 #[serde(rename = "b2")]
627 pub state_of_charge: Option<f32>,
628
629 /// Usable battery size in watt-hours.
630 #[serde(rename = "b3")]
631 pub size: Option<u32>,
632
633 /// Lifetime charge in watt-hours.
634 #[serde(rename = "b4")]
635 pub lifetime_charge: Option<u32>,
636
637 /// Lifetime discharge in watt-hours.
638 #[serde(rename = "b5")]
639 pub lifetime_discharge: Option<u32>,
640
641 /// Battery state.
642 #[serde(rename = "b6")]
643 pub state: Option<BatteryState>,
644}
645
646/// Battery state.
647/// For use with [`AddStatusParams`].
648///
649/// [PVOutput docs](https://pvoutput.org/help/api_specification.html#state-codes)
650// note to future me: deriving repr(int) starts at zero and automatically increments.
651// see https://doc.rust-lang.org/reference/items/enumerations.html#implicit-discriminants
652#[derive(Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize_repr)]
653#[repr(u8)]
654pub enum BatteryState {
655 Invalid,
656 #[default]
657 Standby,
658 ThermalManagement,
659 Enabled,
660 Fault,
661
662 /// User defined. Corresponds to `b2=5`.
663 UserDefinedA,
664
665 /// User defined. Corresponds to `b2=6`.
666 UserDefinedB,
667
668 /// User defined. Corresponds to `b2=7`.
669 UserDefinedC,
670
671 /// User defined. Corresponds to `b2=8`.
672 UserDefinedD,
673
674 /// User defined. Corresponds to `b2=9`.
675 UserDefinedE,
676}
677
678/// Parameters for the [`add_batch_status`](crate::Client::add_batch_status) endpoint.
679#[derive(Clone, Debug, Serialize)]
680pub struct AddBatchStatusParams {
681 /// Parameters common to the [`add_status`](crate::Client::add_status) and
682 /// [`add_batch_status`](crate::Client::add_batch_status) endpoints.
683 #[serde(flatten)]
684 pub common: AddStatusCommon,
685
686 /// Extended data.
687 ///
688 /// Requires [donation status](https://pvoutput.org/help/donations.html).
689 #[serde(flatten)]
690 pub extended_data: Option<AddExtendedParams>,
691}
692
693impl AddBatchStatusParams {
694 /// Creates a new [`AddBatchStatusParams`].
695 #[must_use]
696 pub const fn new(common: AddStatusCommon, extended_data: Option<AddExtendedParams>) -> Self {
697 Self { common, extended_data }
698 }
699}
700
701impl From<AddStatusCommon> for AddBatchStatusParams {
702 fn from(common: AddStatusCommon) -> Self {
703 Self {
704 common,
705 extended_data: None,
706 }
707 }
708}
709
710/// Parameters for the [`add_batch_status_net`](crate::Client::add_batch_status_net) endpoint.
711#[derive(Clone, Debug, Serialize)]
712pub struct AddNetBatchStatusParams {
713 /// The date for this status.
714 #[serde(serialize_with = "jiff_ymd_direct")]
715 pub date: Date,
716
717 /// The time for this status.
718 ///
719 /// Times will be rounded to the nearest configured
720 /// [status interval](https://pvoutput.org/help/live_data.html#live-configuration-status-interval).
721 #[serde(serialize_with = "jiff_hhmm_direct")]
722 pub time: Time,
723
724 spacer_a: (),
725
726 /// Power exported in watts.
727 pub power_exported: u32,
728
729 spacer_b: (),
730
731 /// Power imported in watts.
732 pub power_imported: u32,
733}
734
735impl AddNetBatchStatusParams {
736 /// Creates a new [`AddNetBatchStatusParams`] with the provided fields.
737 #[must_use]
738 pub const fn new(date: Date, time: Time, power_exported: u32, power_imported: u32) -> Self {
739 Self {
740 date,
741 time,
742 spacer_a: (),
743 power_exported,
744 spacer_b: (),
745 power_imported,
746 }
747 }
748}
749
750/// Whether to process a status as cumulative, and if so, which of its fields are cumulative.
751#[derive(Clone, Debug, Copy, Default, Serialize, Hash, Eq, PartialEq)]
752pub enum CumulativeFlag {
753 /// This status is not cumulative.
754 #[serde(rename = "0")]
755 #[default]
756 False,
757
758 /// The energy generation and consumption values for this status are lifetime cumulative values.
759 #[serde(rename = "1")]
760 Both,
761
762 /// The energy generation value for this status is lifetime cumulative.
763 #[serde(rename = "2")]
764 Generation,
765
766 /// The energy consumption value for this status is lifetime cumulative.
767 #[serde(rename = "3")]
768 Consumption,
769}
770
771/// Parameters for the [`post_system`](crate::Client::post_system) endpoint.
772///
773/// Updating extended data variables requires [donation status](https://pvoutput.org/help/donations.html).
774#[derive(Clone, Debug, Default)]
775pub struct PostSystemParams {
776 /// A new name for the system.
777 /// Limited to thirty characters.
778 pub name: Option<String>,
779
780 /// Extended data variables.
781 pub(crate) extended_variables: HashMap<ExtendedVariable, ExtendedVariableUpdate>,
782}
783
784impl PostSystemParams {
785 /// Creates a new [`PostSystemParams`].
786 #[must_use]
787 pub fn new() -> Self { Self::default() }
788
789 /// Creates a new [`PostSystemParams`] with the provided `name` and no extended data variables.
790 #[must_use]
791 pub fn new_rename(name: String) -> Self {
792 Self {
793 name: Some(name),
794 extended_variables: HashMap::new(),
795 }
796 }
797
798 /// Adds a new extended variable update.
799 ///
800 /// If there is already an update for this variable, it will be replaced with the provided update.
801 ///
802 /// # Example
803 /// ```rust
804 /// use pvoutput_client::types::params::{ExtendedVariable, ExtendedVariableUpdate, PostSystemParams};
805 ///
806 /// let mut post = PostSystemParams::default();
807 /// let update = ExtendedVariableUpdate {
808 /// label: Some(String::from("Wind speed")),
809 /// unit: Some(String::from("m/s")),
810 /// ..Default::default()
811 /// };
812 /// post.add_update(ExtendedVariable::V7, update);
813 ///
814 /// assert_eq!(
815 /// post.get_update(ExtendedVariable::V7).map(|u| u.label.as_deref()).flatten(),
816 /// Some("Wind speed")
817 /// );
818 /// assert!(post.get_update(ExtendedVariable::V8).is_none());
819 /// ```
820 pub fn add_update(&mut self, variable: ExtendedVariable, update: ExtendedVariableUpdate) -> &mut Self {
821 self.extended_variables.insert(variable, update);
822 self
823 }
824
825 /// Gets the update for the given extended variable, if any.
826 #[must_use]
827 pub fn get_update(&self, variable: ExtendedVariable) -> Option<&ExtendedVariableUpdate> {
828 self.extended_variables.get(&variable)
829 }
830}
831
832/// An update to an [extended data variable](https://pvoutput.org/help/extended_data.html).
833///
834/// Any omitted fields will use the previously set values if this extended variable already exists, or the default
835/// values if it doesn't.
836///
837/// # Default colours
838/// | Variable | Colour |
839/// |:--------:|--------|
840/// | v7 | 339933 |
841/// | v8 | ccff66 |
842/// | v9 | efcf21 |
843/// | v10 | ff5555 |
844/// | v11 | 0a72b1 |
845/// | v12 | dd5fc9 |
846#[derive(Debug, Clone, Default)]
847pub struct ExtendedVariableUpdate {
848 /// A label for this extended variable.
849 /// Limited to twenty characters.
850 ///
851 /// This field is *required* if this is a new extended variable.
852 pub label: Option<String>,
853
854 /// A unit for this extended variable.
855 /// Limited to ten characters.
856 ///
857 /// This field is *required* if this is a new extended variable.
858 pub unit: Option<String>,
859
860 /// A colour to use for this extended variable, formatted as six lowercase hexadecimal digits without leading
861 /// `#` -- for example, `ff0000` for bright red.
862 pub colour: Option<String>,
863
864 /// The axis to use for this extended variable.
865 /// Must be between zero and five.
866 /// The default is zero.
867 ///
868 /// Parameters placed on the same axis are shown on the same graph scale.
869 pub axis: Option<u8>,
870
871 /// How to graph this extended variable.
872 /// The default is [`Graph::Line`].
873 pub graph: Option<Graph>,
874}
875
876/// An [extended data variable](https://pvoutput.org/help/extended_data.html) identifier.
877#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
878pub enum ExtendedVariable {
879 /// The first extended variable (`v7`).
880 V7 = 7,
881
882 /// The second extended variable (`v8`).
883 V8 = 8,
884
885 /// The third extended variable (`v9`).
886 V9 = 9,
887
888 /// The fourth extended variable (`v10`).
889 V10 = 10,
890
891 /// The fifth extended variable (`v11`).
892 V11 = 11,
893
894 /// The sixth extended variable (`v12`).
895 V12 = 12,
896}
897
898impl TryFrom<u8> for ExtendedVariable {
899 type Error = u8;
900
901 fn try_from(value: u8) -> Result<Self, Self::Error> {
902 match value {
903 7 => Ok(Self::V7),
904 8 => Ok(Self::V8),
905 9 => Ok(Self::V9),
906 10 => Ok(Self::V10),
907 11 => Ok(Self::V11),
908 12 => Ok(Self::V12),
909 _ => Err(value),
910 }
911 }
912}
913
914/// How to graph an extended variable.
915#[derive(Copy, Clone, Debug, Default, Serialize, Hash, PartialEq, Eq)]
916#[serde(rename_all = "lowercase")]
917pub enum Graph {
918 /// Use an area graph.
919 Area,
920
921 /// Use a line graph.
922 #[default]
923 Line,
924}
925
926impl Display for Graph {
927 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
928 match self {
929 Self::Area => write!(f, "area"),
930 Self::Line => write!(f, "line"),
931 }
932 }
933}
934
935#[derive(Serialize)]
936pub(crate) struct DeleteStatusParams {
937 #[serde(serialize_with = "jiff_ymd_direct")]
938 pub d: Date,
939
940 #[serde(serialize_with = "jiff_hhmm")]
941 pub t: Option<Time>,
942}
943
944// custom serialiser functions
945
946/// Serialises an [`Option<Date>`] to a string in the format "yyyymmdd".
947#[allow(clippy::ref_option)]
948fn jiff_ymd<S>(value: &Option<Date>, s: S) -> Result<S::Ok, S::Error>
949where
950 S: Serializer,
951{
952 if let Some(value) = value {
953 s.serialize_str(value.strftime("%Y%m%d").to_string().as_str())
954 } else {
955 s.serialize_none()
956 }
957}
958
959/// Serialises a [`Date`] to a string in the format "yyyymmdd".
960#[allow(clippy::ref_option)]
961fn jiff_ymd_direct<S>(value: &Date, s: S) -> Result<S::Ok, S::Error>
962where
963 S: Serializer,
964{
965 jiff_ymd(&Some(*value), s)
966}
967
968/// Serialises an [`Option<Time>`] to a string in the format "hh:mm".
969#[allow(clippy::ref_option)]
970fn jiff_hhmm<S>(value: &Option<Time>, s: S) -> Result<S::Ok, S::Error>
971where
972 S: Serializer,
973{
974 if let Some(value) = value {
975 s.serialize_str(value.strftime("%H:%M").to_string().as_str())
976 } else {
977 s.serialize_none()
978 }
979}
980
981/// Serialises a [`Time`] to a string in the format "hh:mm".
982#[allow(clippy::ref_option)]
983fn jiff_hhmm_direct<S>(value: &Time, s: S) -> Result<S::Ok, S::Error>
984where
985 S: Serializer,
986{
987 jiff_hhmm(&Some(*value), s)
988}
989
990/// Serialises a [`bool`] to a string "1" or "0".
991#[allow(clippy::trivially_copy_pass_by_ref)]
992fn bool_int<S>(value: &bool, s: S) -> Result<S::Ok, S::Error>
993where
994 S: Serializer,
995{
996 s.serialize_u8(u8::from(*value))
997}
998
999/// Serialises a latitude and longitude pair to a comma-separated string.
1000#[allow(clippy::ref_option)]
1001fn lat_long<S>(value: &Option<(f32, f32)>, s: S) -> Result<S::Ok, S::Error>
1002where
1003 S: Serializer,
1004{
1005 if let Some(value) = value {
1006 s.serialize_str(format!("{},{}", value.0, value.1).as_str())
1007 } else {
1008 s.serialize_none()
1009 }
1010}