openadr3-client-gac-compliance 1.1.0__py3-none-any.whl → 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,12 +13,16 @@ as the program object does not contain the events, these are stored seperately i
13
13
 
14
14
  from itertools import pairwise
15
15
  import re
16
+ from typing import Tuple
16
17
  from openadr3_client.models.model import ValidatorRegistry, Model as ValidatorModel
17
18
  from openadr3_client.models.event.event import Event
18
19
  from openadr3_client.models.event.event_payload import EventPayloadType
19
20
 
21
+ from pydantic import ValidationError
22
+ from pydantic_core import InitErrorDetails, PydanticCustomError
20
23
 
21
- def _continuous_or_seperated(self: Event) -> Event:
24
+
25
+ def _continuous_or_seperated(self: Event) -> Tuple[Event, list[InitErrorDetails]]:
22
26
  """Enforces that events either have consistent interval definitions compliant with GAC.
23
27
 
24
28
  the Grid aware charging (GAC) specification allows for two types of (mutually exclusive)
@@ -35,6 +39,8 @@ def _continuous_or_seperated(self: Event) -> Event:
35
39
  and the top-level intervalPeriod of the event must be None. This seperated approach is used when events have differing
36
40
  durations.
37
41
  """
42
+ validation_errors: list[InitErrorDetails] = []
43
+
38
44
  intervals = self.intervals or ()
39
45
 
40
46
  if self.interval_period is None:
@@ -42,8 +48,16 @@ def _continuous_or_seperated(self: Event) -> Event:
42
48
  # Ensure that all intervals have the interval_period defined, to comply with the GAC specification.
43
49
  undefined_intervals_period = [i for i in intervals if i.interval_period is None]
44
50
  if undefined_intervals_period:
45
- raise ValueError(
46
- "Either 'interval_period' must be set on the event once, or every interval must have its own 'interval_period'."
51
+ validation_errors.append(
52
+ InitErrorDetails(
53
+ type=PydanticCustomError(
54
+ "value_error",
55
+ "Either 'interval_period' must be set on the event once, or every interval must have its own 'interval_period'.",
56
+ ),
57
+ loc=("intervals",),
58
+ input=self.intervals,
59
+ ctx={},
60
+ )
47
61
  )
48
62
  else:
49
63
  # interval period set at top level of the event.
@@ -52,14 +66,22 @@ def _continuous_or_seperated(self: Event) -> Event:
52
66
  i for i in intervals if i.interval_period is not None
53
67
  ]
54
68
  if duplicate_interval_period:
55
- raise ValueError(
56
- "Either 'interval_period' must be set on the event once, or every interval must have its own 'interval_period'."
69
+ validation_errors.append(
70
+ InitErrorDetails(
71
+ type=PydanticCustomError(
72
+ "value_error",
73
+ "Either 'interval_period' must be set on the event once, or every interval must have its own 'interval_period'.",
74
+ ),
75
+ loc=("intervals",),
76
+ input=self.intervals,
77
+ ctx={},
78
+ )
57
79
  )
58
80
 
59
- return self
81
+ return self, validation_errors
60
82
 
61
83
 
62
- def _targets_compliant(self: Event) -> Event:
84
+ def _targets_compliant(self: Event) -> Tuple[Event, list[InitErrorDetails]]:
63
85
  """Enforces that the targets of the event are compliant with GAC.
64
86
 
65
87
  GAC enforces the following constraints for targets:
@@ -69,6 +91,7 @@ def _targets_compliant(self: Event) -> Event:
69
91
  - The event must contain a VEN_NAME target.
70
92
  - The VEN_NAME target value must be a list of 'ven object name' values (between 1 and 128 characters).
71
93
  """
94
+ validation_errors: list[InitErrorDetails] = []
72
95
  targets = self.targets or ()
73
96
 
74
97
  power_service_locations = [
@@ -77,42 +100,124 @@ def _targets_compliant(self: Event) -> Event:
77
100
  ven_names = [t for t in targets if t.type == "VEN_NAME"]
78
101
 
79
102
  if not power_service_locations:
80
- raise ValueError("The event must contain a POWER_SERVICE_LOCATIONS target.")
103
+ validation_errors.append(
104
+ InitErrorDetails(
105
+ type=PydanticCustomError(
106
+ "value_error",
107
+ "The event must contain a POWER_SERVICE_LOCATIONS target.",
108
+ ),
109
+ loc=("targets",),
110
+ input=self.targets,
111
+ ctx={},
112
+ )
113
+ )
81
114
 
82
115
  if not ven_names:
83
- raise ValueError("The event must contain a VEN_NAME target.")
116
+ validation_errors.append(
117
+ InitErrorDetails(
118
+ type=PydanticCustomError(
119
+ "value_error",
120
+ "The event must contain a VEN_NAME target.",
121
+ ),
122
+ loc=("targets",),
123
+ input=self.targets,
124
+ ctx={},
125
+ )
126
+ )
84
127
 
85
128
  if len(power_service_locations) > 1:
86
- raise ValueError(
87
- "The event must contain exactly one POWER_SERVICE_LOCATIONS target."
129
+ validation_errors.append(
130
+ InitErrorDetails(
131
+ type=PydanticCustomError(
132
+ "value_error",
133
+ "The event must contain exactly one POWER_SERVICE_LOCATIONS target.",
134
+ ),
135
+ loc=("targets",),
136
+ input=self.targets,
137
+ ctx={},
138
+ )
88
139
  )
89
140
 
90
141
  if len(ven_names) > 1:
91
- raise ValueError("The event must contain only one VEN_NAME target.")
92
-
93
- power_service_location = power_service_locations[0]
94
- ven_name = ven_names[0]
142
+ validation_errors.append(
143
+ InitErrorDetails(
144
+ type=PydanticCustomError(
145
+ "value_error",
146
+ "The event must contain only one VEN_NAME target.",
147
+ ),
148
+ loc=("targets",),
149
+ input=self.targets,
150
+ ctx={},
151
+ )
152
+ )
95
153
 
96
- if len(power_service_location.values) == 0:
97
- raise ValueError("The POWER_SERVICE_LOCATIONS target value cannot be empty.")
154
+ if (
155
+ power_service_locations
156
+ and ven_names
157
+ and len(power_service_locations) == 1
158
+ and len(ven_names) == 1
159
+ ):
160
+ power_service_location = power_service_locations[0]
161
+ ven_name = ven_names[0]
162
+
163
+ if len(power_service_location.values) == 0:
164
+ validation_errors.append(
165
+ InitErrorDetails(
166
+ type=PydanticCustomError(
167
+ "value_error",
168
+ "The POWER_SERVICE_LOCATIONS target value cannot be empty.",
169
+ ),
170
+ loc=("targets",),
171
+ input=self.targets,
172
+ ctx={},
173
+ )
174
+ )
98
175
 
99
- if not all(re.fullmatch(r"\d{18}", v) for v in power_service_location.values):
100
- raise ValueError(
101
- "The POWER_SERVICE_LOCATIONS target value must be a list of 'EAN18' values."
102
- )
176
+ if not all(re.fullmatch(r"\d{18}", v) for v in power_service_location.values):
177
+ validation_errors.append(
178
+ InitErrorDetails(
179
+ type=PydanticCustomError(
180
+ "value_error",
181
+ "The POWER_SERVICE_LOCATIONS target value must be a list of 'EAN18' values.",
182
+ ),
183
+ loc=("targets",),
184
+ input=self.targets,
185
+ ctx={},
186
+ )
187
+ )
103
188
 
104
- if len(ven_name.values) == 0:
105
- raise ValueError("The VEN_NAME target value cannot be empty.")
189
+ if len(ven_name.values) == 0:
190
+ validation_errors.append(
191
+ InitErrorDetails(
192
+ type=PydanticCustomError(
193
+ "value_error",
194
+ "The VEN_NAME target value cannot be empty.",
195
+ ),
196
+ loc=("targets",),
197
+ input=self.targets,
198
+ ctx={},
199
+ )
200
+ )
106
201
 
107
- if not all(1 <= len(v) <= 128 for v in ven_name.values):
108
- raise ValueError(
109
- "The VEN_NAME target value must be a list of 'ven object name' values (between 1 and 128 characters)."
110
- )
202
+ if not all(1 <= len(v) <= 128 for v in ven_name.values):
203
+ validation_errors.append(
204
+ InitErrorDetails(
205
+ type=PydanticCustomError(
206
+ "value_error",
207
+ "The VEN_NAME target value must be a list of 'ven object name' values (between 1 and 128 characters).",
208
+ ),
209
+ loc=("targets",),
210
+ input=self.targets,
211
+ ctx={},
212
+ )
213
+ )
111
214
 
112
- return self
215
+ return self, validation_errors
113
216
 
114
217
 
115
- def _payload_descriptor_gac_compliant(self: Event) -> Event:
218
+ def _payload_descriptor_gac_compliant(
219
+ self: Event,
220
+ ) -> Tuple[Event, list[InitErrorDetails]]:
116
221
  """Enforces that the payload descriptor is GAC compliant.
117
222
 
118
223
  GAC enforces the following constraints for payload descriptors:
@@ -121,28 +226,67 @@ def _payload_descriptor_gac_compliant(self: Event) -> Event:
121
226
  - The payload descriptor must have a payload type of 'IMPORT_CAPACITY_LIMIT'
122
227
  - The payload descriptor must have a units of 'KW' (case sensitive).
123
228
  """
124
- if self.payload_descriptor is None:
125
- raise ValueError("The event must have a payload descriptor.")
229
+ validation_errors: list[InitErrorDetails] = []
126
230
 
127
- if len(self.payload_descriptor) != 1:
128
- raise ValueError("The event must have exactly one payload descriptor.")
231
+ if self.payload_descriptor is None:
232
+ validation_errors.append(
233
+ InitErrorDetails(
234
+ type=PydanticCustomError(
235
+ "value_error",
236
+ "The event must have a payload descriptor.",
237
+ ),
238
+ loc=("payload_descriptor",),
239
+ input=self.payload_descriptor,
240
+ ctx={},
241
+ )
242
+ )
129
243
 
130
- payload_descriptor = self.payload_descriptor[0]
244
+ if self.payload_descriptor is not None:
245
+ if len(self.payload_descriptor) != 1:
246
+ validation_errors.append(
247
+ InitErrorDetails(
248
+ type=PydanticCustomError(
249
+ "value_error",
250
+ "The event must have exactly one payload descriptor.",
251
+ ),
252
+ loc=("payload_descriptor",),
253
+ input=self.payload_descriptor,
254
+ ctx={},
255
+ )
256
+ )
131
257
 
132
- if payload_descriptor.payload_type != EventPayloadType.IMPORT_CAPACITY_LIMIT:
133
- raise ValueError(
134
- "The payload descriptor must have a payload type of 'IMPORT_CAPACITY_LIMIT'."
135
- )
258
+ payload_descriptor = self.payload_descriptor[0]
259
+
260
+ if payload_descriptor.payload_type != EventPayloadType.IMPORT_CAPACITY_LIMIT:
261
+ validation_errors.append(
262
+ InitErrorDetails(
263
+ type=PydanticCustomError(
264
+ "value_error",
265
+ "The payload descriptor must have a payload type of 'IMPORT_CAPACITY_LIMIT'.",
266
+ ),
267
+ loc=("payload_descriptor",),
268
+ input=self.payload_descriptor,
269
+ ctx={},
270
+ )
271
+ )
136
272
 
137
- if payload_descriptor.units != "KW":
138
- raise ValueError(
139
- "The payload descriptor must have a units of 'KW' (case sensitive)."
140
- )
273
+ if payload_descriptor.units != "KW":
274
+ validation_errors.append(
275
+ InitErrorDetails(
276
+ type=PydanticCustomError(
277
+ "value_error",
278
+ "The payload descriptor must have a units of 'KW' (case sensitive).",
279
+ ),
280
+ loc=("payload_descriptor",),
281
+ input=self.payload_descriptor,
282
+ ctx={},
283
+ )
284
+ )
141
285
 
142
- return self
286
+ return self, validation_errors
143
287
 
144
288
 
145
- def _event_interval_gac_compliant(self: Event) -> Event:
289
+ def _event_interval_gac_compliant(self: Event) -> Tuple[Event, list[InitErrorDetails]]:
146
290
  """Enforces that the event interval is GAC compliant.
147
291
 
148
292
  GAC enforces the following constraints for event intervals:
@@ -151,29 +295,77 @@ def _event_interval_gac_compliant(self: Event) -> Event:
151
295
  - The event interval must have exactly one payload.
152
296
  - The payload of the event interval must have a type of 'IMPORT_CAPACITY_LIMIT'
153
297
  """
298
+ validation_errors: list[InitErrorDetails] = []
299
+
154
300
  if not self.intervals:
155
- raise ValueError("The event must have at least one interval.")
301
+ validation_errors.append(
302
+ InitErrorDetails(
303
+ type=PydanticCustomError(
304
+ "value_error",
305
+ "The event must have at least one interval.",
306
+ ),
307
+ loc=("intervals",),
308
+ input=self.intervals,
309
+ ctx={},
310
+ )
311
+ )
156
312
 
157
313
  if not all(curr.id > prev.id for prev, curr in pairwise(self.intervals)):
158
- raise ValueError(
159
- "The event interval must have an id value that is strictly increasing."
314
+ validation_errors.append(
315
+ InitErrorDetails(
316
+ type=PydanticCustomError(
317
+ "value_error",
318
+ "The event interval must have an id value that is strictly increasing.",
319
+ ),
320
+ loc=("intervals",),
321
+ input=self.intervals,
322
+ ctx={},
323
+ )
160
324
  )
161
325
 
162
326
  for interval in self.intervals:
163
327
  if interval.payloads is None:
164
- raise ValueError("The event interval must have a payload.")
328
+ validation_errors.append(
329
+ InitErrorDetails(
330
+ type=PydanticCustomError(
331
+ "value_error",
332
+ "The event interval must have a payload.",
333
+ ),
334
+ loc=("intervals",),
335
+ input=self.intervals,
336
+ ctx={},
337
+ )
338
+ )
165
339
 
166
340
  if len(interval.payloads) != 1:
167
- raise ValueError("The event interval must have exactly one payload.")
168
-
169
- payload = interval.payloads[0]
170
-
171
- if payload.type != EventPayloadType.IMPORT_CAPACITY_LIMIT:
172
- raise ValueError(
173
- "The event interval payload must have a payload type of 'IMPORT_CAPACITY_LIMIT'."
341
+ validation_errors.append(
342
+ InitErrorDetails(
343
+ type=PydanticCustomError(
344
+ "value_error",
345
+ "The event interval must have exactly one payload.",
346
+ ),
347
+ loc=("intervals",),
348
+ input=self.intervals,
349
+ ctx={},
350
+ )
174
351
  )
352
+ else:
353
+ payload = interval.payloads[0]
354
+
355
+ if payload.type != EventPayloadType.IMPORT_CAPACITY_LIMIT:
356
+ validation_errors.append(
357
+ InitErrorDetails(
358
+ type=PydanticCustomError(
359
+ "value_error",
360
+ "The event interval payload must have a payload type of 'IMPORT_CAPACITY_LIMIT'.",
361
+ ),
362
+ loc=("intervals",),
363
+ input=self.intervals,
364
+ ctx={},
365
+ )
366
+ )
175
367
 
176
- return self
368
+ return self, validation_errors
177
369
 
178
370
 
179
371
  @ValidatorRegistry.register(Event, ValidatorModel())
@@ -185,16 +377,40 @@ def event_gac_compliant(self: Event) -> Event:
185
377
  - The event must not have a priority set.
186
378
  - The event must have either a continuous or seperated interval definition.
187
379
  """
380
+ validation_errors: list[InitErrorDetails] = []
381
+
188
382
  if self.priority is not None:
189
- raise ValueError(
190
- "The event must not have a priority set for GAC 2.0 compliance"
383
+ validation_errors.append(
384
+ InitErrorDetails(
385
+ type=PydanticCustomError(
386
+ "value_error",
387
+ "The event must not have a priority set for GAC 2.0 compliance",
388
+ ),
389
+ loc=("priority",),
390
+ input=self.priority,
391
+ ctx={},
392
+ )
191
393
  )
192
394
 
193
- interval_periods_validated = _continuous_or_seperated(self)
194
- targets_validated = _targets_compliant(interval_periods_validated)
195
- payload_descriptor_validated = _payload_descriptor_gac_compliant(targets_validated)
196
- event_interval_validated = _event_interval_gac_compliant(
395
+ interval_periods_validated, errors = _continuous_or_seperated(self)
396
+ validation_errors.extend(errors)
397
+
398
+ targets_validated, errors = _targets_compliant(interval_periods_validated)
399
+ validation_errors.extend(errors)
400
+
401
+ payload_descriptor_validated, errors = _payload_descriptor_gac_compliant(
402
+ targets_validated
403
+ )
404
+ validation_errors.extend(errors)
405
+
406
+ event_interval_validated, errors = _event_interval_gac_compliant(
197
407
  payload_descriptor_validated
198
408
  )
409
+ validation_errors.extend(errors)
410
+
411
+ if validation_errors:
412
+ raise ValidationError.from_exception_data(
413
+ title=self.__class__.__name__, line_errors=validation_errors
414
+ )
199
415
 
200
416
  return event_interval_validated
@@ -5,6 +5,9 @@ from openadr3_client.models.model import ValidatorRegistry, Model as ValidatorMo
5
5
 
6
6
  import re
7
7
 
8
+ from pydantic import ValidationError
9
+ from pydantic_core import InitErrorDetails, PydanticCustomError
10
+
8
11
 
9
12
  @ValidatorRegistry.register(Program, ValidatorModel())
10
13
  def program_gac_compliant(self: Program) -> Program:
@@ -18,22 +21,81 @@ def program_gac_compliant(self: Program) -> Program:
18
21
  - The program MUST have bindingEvents set to True.
19
22
  are allowed there.
20
23
  """
24
+ validation_errors: list[InitErrorDetails] = []
25
+
21
26
  program_type_regex = r"^DSO_CPO_INTERFACE-(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
22
27
 
23
28
  if self.retailer_name is None:
24
- raise ValueError("The program must have a retailer name.")
29
+ validation_errors.append(
30
+ InitErrorDetails(
31
+ type=PydanticCustomError(
32
+ "value_error",
33
+ "The program must have a retailer name.",
34
+ ),
35
+ loc=("retailer_name",),
36
+ input=self.retailer_name,
37
+ ctx={},
38
+ )
39
+ )
25
40
 
26
- if len(self.retailer_name) < 2 or len(self.retailer_name) > 128:
27
- raise ValueError("The retailer name must be between 2 and 128 characters long.")
41
+ if self.retailer_name is not None and (
42
+ len(self.retailer_name) < 2 or len(self.retailer_name) > 128
43
+ ):
44
+ validation_errors.append(
45
+ InitErrorDetails(
46
+ type=PydanticCustomError(
47
+ "value_error",
48
+ "The retailer name must be between 2 and 128 characters long.",
49
+ ),
50
+ loc=("retailer_name",),
51
+ input=self.retailer_name,
52
+ ctx={},
53
+ )
54
+ )
28
55
 
29
56
  if self.program_type is None:
30
- raise ValueError("The program must have a program type.")
31
- if not re.fullmatch(program_type_regex, self.program_type):
32
- raise ValueError(
33
- "The program type must follow the format DSO_CPO_INTERFACE-x.x.x."
57
+ validation_errors.append(
58
+ InitErrorDetails(
59
+ type=PydanticCustomError(
60
+ "value_error",
61
+ "The program must have a program type.",
62
+ ),
63
+ loc=("program_type",),
64
+ input=self.program_type,
65
+ ctx={},
66
+ )
67
+ )
68
+ if self.program_type is not None and not re.fullmatch(
69
+ program_type_regex, self.program_type
70
+ ):
71
+ validation_errors.append(
72
+ InitErrorDetails(
73
+ type=PydanticCustomError(
74
+ "value_error",
75
+ "The program type must follow the format DSO_CPO_INTERFACE-x.x.x.",
76
+ ),
77
+ loc=("program_type",),
78
+ input=self.program_type,
79
+ ctx={},
80
+ )
34
81
  )
35
82
 
36
83
  if self.binding_events is False:
37
- raise ValueError("The program must have bindingEvents set to True.")
84
+ validation_errors.append(
85
+ InitErrorDetails(
86
+ type=PydanticCustomError(
87
+ "value_error",
88
+ "The program must have bindingEvents set to True.",
89
+ ),
90
+ loc=("binding_events",),
91
+ input=self.binding_events,
92
+ ctx={},
93
+ )
94
+ )
95
+
96
+ if validation_errors:
97
+ raise ValidationError.from_exception_data(
98
+ title=self.__class__.__name__, line_errors=validation_errors
99
+ )
38
100
 
39
101
  return self
@@ -3,6 +3,9 @@ from openadr3_client.models.model import ValidatorRegistry, Model as ValidatorMo
3
3
  from openadr3_client.models.ven.ven import Ven
4
4
  import pycountry
5
5
 
6
+ from pydantic import ValidationError
7
+ from pydantic_core import InitErrorDetails, PydanticCustomError
8
+
6
9
 
7
10
  @ValidatorRegistry.register(Ven, ValidatorModel())
8
11
  def ven_gac_compliant(self: Ven) -> Ven:
@@ -12,16 +15,41 @@ def ven_gac_compliant(self: Ven) -> Ven:
12
15
  - The ven must have a ven name
13
16
  - The ven name must be an eMI3 identifier.
14
17
  """
18
+ validation_errors: list[InitErrorDetails] = []
19
+
15
20
  emi3_identifier_regex = r"^[A-Z]{2}-?[A-Z0-9]{3}$"
16
21
 
17
22
  if not re.fullmatch(emi3_identifier_regex, self.ven_name):
18
- raise ValueError("The ven name must be formatted as an eMI3 identifier.")
23
+ validation_errors.append(
24
+ InitErrorDetails(
25
+ type=PydanticCustomError(
26
+ "value_error",
27
+ "The ven name must be formatted as an eMI3 identifier.",
28
+ ),
29
+ loc=("ven_name",),
30
+ input=self.ven_name,
31
+ ctx={},
32
+ )
33
+ )
19
34
 
20
35
  alpha_2_country = pycountry.countries.get(alpha_2=self.ven_name[:2])
21
36
 
22
37
  if alpha_2_country is None:
23
- raise ValueError(
24
- "The first two characters of the ven name must be a valid ISO 3166-1 alpha-2 country code."
38
+ validation_errors.append(
39
+ InitErrorDetails(
40
+ type=PydanticCustomError(
41
+ "value_error",
42
+ "The first two characters of the ven name must be a valid ISO 3166-1 alpha-2 country code.",
43
+ ),
44
+ loc=("ven_name",),
45
+ input=self.ven_name,
46
+ ctx={},
47
+ )
48
+ )
49
+
50
+ if validation_errors:
51
+ raise ValidationError.from_exception_data(
52
+ title=self.__class__.__name__, line_errors=validation_errors
25
53
  )
26
54
 
27
55
  return self
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openadr3-client-gac-compliance
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary:
5
5
  Author: Nick van der Burgt
6
6
  Author-email: nick.van.der.burgt@elaad.nl
@@ -8,7 +8,7 @@ Requires-Python: >=3.12, <4
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
10
  Classifier: Programming Language :: Python :: 3.13
11
- Requires-Dist: openadr3-client (>=0.0.1,<0.0.2)
11
+ Requires-Dist: openadr3-client (>=0.0.1,<1.0.0)
12
12
  Requires-Dist: pycountry (>=24.6.1,<25.0.0)
13
13
  Requires-Dist: pydantic (>=2.11.2,<3.0.0)
14
14
  Description-Content-Type: text/markdown
@@ -1,11 +1,11 @@
1
1
  openadr3_client_gac_compliance/__init__.py,sha256=dzf9YdOlssEb9GSIoThaMzEJ956G-hpLi7HIXRC3TR8,334
2
2
  openadr3_client_gac_compliance/config.py,sha256=X_KEl99bUm05rH0IOKLyeR4Zn2utdC8U3vLkdG8MYXU,675
3
3
  openadr3_client_gac_compliance/gac20/__init__.py,sha256=YPjRHMl-uR6ZwrDGjY_EboScHe_VhTrovso_zDOSd6o,220
4
- openadr3_client_gac_compliance/gac20/event_gac_compliant.py,sha256=KPcUyazlroFv0c3IovvtMW4a4LD2lBOyct4ptjngixk,7970
5
- openadr3_client_gac_compliance/gac20/program_gac_compliant.py,sha256=qJeyvAdu0f6F0I3iXXl64dOVRZwPfRHQyGqc3ECuzjw,1725
6
- openadr3_client_gac_compliance/gac20/ven_gac_compliant.py,sha256=YAzM2Q9ggh_O-p1xD5GlN7-lTGg2MjqUG26KBSy3-d8,908
4
+ openadr3_client_gac_compliance/gac20/event_gac_compliant.py,sha256=dZIVRRgpRajkceu8IDLNaz9b0lNmlFfWjdlAvlxymTo,15060
5
+ openadr3_client_gac_compliance/gac20/program_gac_compliant.py,sha256=i7l9oboA31_HMHZ-b8GbVTXGfeNCL3kF-o5e7--riCg,3540
6
+ openadr3_client_gac_compliance/gac20/ven_gac_compliant.py,sha256=57MH73JorFSNyeB6Tep4XX_WMhtKls2DXoSoBaMhob8,1751
7
7
  openadr3_client_gac_compliance/gac21/__init__.py,sha256=GTu1EyBj2T72zT3MKPettcvP-DNCJ0p6oAMj3Z-08N0,61
8
- openadr3_client_gac_compliance-1.1.0.dist-info/LICENSE.md,sha256=NNNxKzhSK6afX-UaN8WZIVtMi5Tu8dVr6q3ciTSnH-g,10250
9
- openadr3_client_gac_compliance-1.1.0.dist-info/METADATA,sha256=tuuieVH5mgB1rE9ijxwSyt3zSFzvB4zqEwsw98WLA-s,1096
10
- openadr3_client_gac_compliance-1.1.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
11
- openadr3_client_gac_compliance-1.1.0.dist-info/RECORD,,
8
+ openadr3_client_gac_compliance-1.2.1.dist-info/LICENSE.md,sha256=NNNxKzhSK6afX-UaN8WZIVtMi5Tu8dVr6q3ciTSnH-g,10250
9
+ openadr3_client_gac_compliance-1.2.1.dist-info/METADATA,sha256=n5GMDEvlXrEreakDfUWYiVOTZF7mgT1W8JzzhhsGK6g,1096
10
+ openadr3_client_gac_compliance-1.2.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
11
+ openadr3_client_gac_compliance-1.2.1.dist-info/RECORD,,