From d102fb52dad3d67b4e548df7c1428c57890e1061 Mon Sep 17 00:00:00 2001 From: William AFONSO <56768236+will-afs@users.noreply.github.com> Date: Wed, 22 Mar 2023 17:35:37 +0100 Subject: [PATCH] Add custom_data attribute to payload-related dataclasses (#434) OCPP-2.0.1_part2_specification_edition2 states: 'A CustomData element exists as an optional element in the JSON schemas of all types. CustomData is the only class in the JSON schema files that allows additional properties. It can thus be used to add additional custom attributes to any type.' With some more details, provided in OCPP-2.0.1_part4_ocpp-j-specification: 'Whereas the standard HeartbeatRequest has an empty body, a customized version, that provides the value of the main meter and a count of all sessions to date, could look like this: { "customData": { "vendorId": "com.mycompany.customheartbeat", "mainMeterValue": 12345, "sessionsToDate": 342 } } A CSMS that has implemented this extension, identified by its "vendorId", will be able to process the data. Other CSMS implementations will simply ignore these custom properties.' OCPP 2.0.1 specific OCPP 1.6, for its part, does define the same concept of enabling the exchange of custom information between a CS and a CSMS, but through dedicated messages: please refer to "4.3. Data Transfer" in ocpp-1.6 edition 2. Why not a Payload dataclass from which other payload dataclasses could inherit I was thinking to just create a Payload dataclass, which all OCPP payloads (in ocpp.v201.call.py and ocpp.v201.call_result.py) could inherit from: from dataclasses import dataclass, field from typing import Any, Dict @dataclass class Payload: custom_data: Optional[Dict[str, Any]] = field(default=None, init=False) Example: @dataclass class StatusNotificationPayload(Payload): timestamp: str connector_status: str evse_id: int connector_id: int But the thing is, TMH currently uses Python>=3.7. And before Python 3.10, inheritance with dataclasses is quite messy: with the approach proposed above, as custom_data is a default argument, payload classes inheriting from Payload would require having all of their attributes defining default values, which we obviously don't want. So, instead, I have resignated myself to modify all OCPP payloads directly to define a custom_data: Optional[Dict[str, Any]] = None attribute for each payload class in ocpp.v201.call.py and ocpp.v201.call_result.py. --- ocpp/v201/call.py | 69 +++++++++++++++++++++++++++++++-- ocpp/v201/call_result.py | 82 +++++++++++++++++++++++++++++++--------- 2 files changed, 130 insertions(+), 21 deletions(-) diff --git a/ocpp/v201/call.py b/ocpp/v201/call.py index ae2149f16..f97f9a1e1 100644 --- a/ocpp/v201/call.py +++ b/ocpp/v201/call.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional @dataclass @@ -7,62 +7,72 @@ class AuthorizePayload: id_token: Dict certificate: Optional[str] = None iso15118_certificate_hash_data: Optional[List] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class BootNotificationPayload: charging_station: Dict reason: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class CancelReservationPayload: reservation_id: int + custom_data: Optional[Dict[str, Any]] = None @dataclass class CertificateSignedPayload: certificate_chain: str certificate_type: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ChangeAvailabilityPayload: operational_status: str evse: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearCachePayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearChargingProfilePayload: charging_profile_id: Optional[int] = None charging_profile_criteria: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearDisplayMessagePayload: id: int + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearVariableMonitoringPayload: id: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearedChargingLimitPayload: charging_limit_source: str evse_id: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class CostUpdatedPayload: total_cost: int transaction_id: str + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -73,6 +83,7 @@ class CustomerInformationPayload: customer_certificate: Optional[Dict] = None id_token: Optional[Dict] = None customer_identifier: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -80,17 +91,20 @@ class DataTransferPayload: vendor_id: str message_id: Optional[str] = None data: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class DeleteCertificatePayload: certificate_hash_data: Dict + custom_data: Optional[Dict[str, Any]] = None @dataclass class FirmwareStatusNotificationPayload: status: str request_id: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -98,17 +112,20 @@ class Get15118EVCertificatePayload: iso15118_schema_version: str action: str exi_request: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetBaseReportPayload: request_id: int report_base: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetCertificateStatusPayload: ocsp_request_data: Dict + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -116,6 +133,7 @@ class GetChargingProfilesPayload: request_id: int charging_profile: Dict evse_id: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -123,6 +141,7 @@ class GetCompositeSchedulePayload: duration: int evse_id: int charging_rate_unit: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -131,16 +150,18 @@ class GetDisplayMessagesPayload: id: Optional[List] = None priority: Optional[str] = None state: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetInstalledCertificateIdsPayload: certificate_type: Optional[List] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetLocalListVersionPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -150,6 +171,7 @@ class GetLogPayload: request_id: int retries: Optional[int] = None retry_interval: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -157,6 +179,7 @@ class GetMonitoringReportPayload: request_id: int component_variable: Optional[List] = None monitoring_criteria: Optional[List] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -164,39 +187,45 @@ class GetReportPayload: request_id: int component_variable: Optional[List] = None component_criteria: Optional[List] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetTransactionStatusPayload: transaction_id: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetVariablesPayload: get_variable_data: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class HeartbeatPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class InstallCertificatePayload: certificate_type: str certificate: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class LogStatusNotificationPayload: status: str request_id: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class MeterValuesPayload: evse_id: int meter_value: List + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -204,6 +233,7 @@ class NotifyChargingLimitPayload: charging_limit: Dict charging_schedule: Optional[List] = None evse_id: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -213,6 +243,7 @@ class NotifyCustomerInformationPayload: generated_at: str request_id: int tbc: Optional[bool] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -220,6 +251,7 @@ class NotifyDisplayMessagesPayload: request_id: int message_info: Optional[List] = None tbc: Optional[bool] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -227,6 +259,7 @@ class NotifyEVChargingNeedsPayload: charging_needs: Dict evse_id: int max_schedule_tuples: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -234,6 +267,7 @@ class NotifyEVChargingSchedulePayload: time_base: str charging_schedule: Dict evse_id: int + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -242,6 +276,7 @@ class NotifyEventPayload: seq_no: int event_data: List tbc: Optional[bool] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -251,6 +286,7 @@ class NotifyMonitoringReportPayload: generated_at: str monitor: Optional[List] = None tbc: Optional[bool] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -260,6 +296,7 @@ class NotifyReportPayload: seq_no: int report_data: Optional[List] = None tbc: Optional[bool] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -269,6 +306,7 @@ class PublishFirmwarePayload: request_id: int retries: Optional[int] = None retry_interval: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -276,6 +314,7 @@ class PublishFirmwareStatusNotificationPayload: status: str location: Optional[List] = None request_id: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -285,6 +324,7 @@ class ReportChargingProfilesPayload: charging_profile: List evse_id: int tbc: Optional[bool] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -294,17 +334,20 @@ class RequestStartTransactionPayload: evse_id: Optional[int] = None group_id_token: Optional[Dict] = None charging_profile: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class RequestStopTransactionPayload: transaction_id: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class ReservationStatusUpdatePayload: reservation_id: int reservation_update_status: str + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -315,12 +358,14 @@ class ReserveNowPayload: connector_type: Optional[str] = None evse_id: Optional[int] = None group_id_token: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ResetPayload: type: str evse_id: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -328,6 +373,7 @@ class SecurityEventNotificationPayload: type: str timestamp: str tech_info: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -335,49 +381,58 @@ class SendLocalListPayload: version_number: int update_type: str local_authorization_list: Optional[List] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetChargingProfilePayload: evse_id: int charging_profile: Dict + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetDisplayMessagePayload: message: Dict + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetMonitoringBasePayload: monitoring_base: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetMonitoringLevelPayload: severity: int + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetNetworkProfilePayload: configuration_slot: int connection_data: Dict + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetVariableMonitoringPayload: set_monitoring_data: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetVariablesPayload: set_variable_data: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class SignCertificatePayload: csr: str certificate_type: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -386,6 +441,7 @@ class StatusNotificationPayload: connector_status: str evse_id: int connector_id: int + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -402,23 +458,27 @@ class TransactionEventPayload: reservation_id: Optional[int] = None evse: Optional[Dict] = None id_token: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class TriggerMessagePayload: requested_message: str evse: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class UnlockConnectorPayload: evse_id: int connector_id: int + custom_data: Optional[Dict[str, Any]] = None @dataclass class UnpublishFirmwarePayload: checksum: str + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -427,3 +487,4 @@ class UpdateFirmwarePayload: firmware: Dict retries: Optional[int] = None retry_interval: Optional[int] = None + custom_data: Optional[Dict[str, Any]] = None diff --git a/ocpp/v201/call_result.py b/ocpp/v201/call_result.py index 409d5ecb1..6f3b25f0b 100644 --- a/ocpp/v201/call_result.py +++ b/ocpp/v201/call_result.py @@ -1,11 +1,12 @@ from dataclasses import dataclass -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional @dataclass class AuthorizePayload: id_token_info: Dict certificate_status: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -14,63 +15,72 @@ class BootNotificationPayload: interval: int status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class CancelReservationPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class CertificateSignedPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ChangeAvailabilityPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearCachePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearChargingProfilePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearDisplayMessagePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearVariableMonitoringPayload: clear_monitoring_result: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class ClearedChargingLimitPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class CostUpdatedPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class CustomerInformationPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -78,17 +88,19 @@ class DataTransferPayload: status: str status_info: Optional[Dict] = None data: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class DeleteCertificatePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class FirmwareStatusNotificationPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -96,12 +108,14 @@ class Get15118EVCertificatePayload: status: str exi_response: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetBaseReportPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -109,12 +123,14 @@ class GetCertificateStatusPayload: status: str status_info: Optional[Dict] = None ocsp_result: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetChargingProfilesPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -122,12 +138,14 @@ class GetCompositeSchedulePayload: status: str status_info: Optional[Dict] = None schedule: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetDisplayMessagesPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -135,11 +153,13 @@ class GetInstalledCertificateIdsPayload: status: str status_info: Optional[Dict] = None certificate_hash_data_chain: Optional[List] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetLocalListVersionPayload: version_number: int + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -147,108 +167,118 @@ class GetLogPayload: status: str status_info: Optional[Dict] = None filename: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetMonitoringReportPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetReportPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetTransactionStatusPayload: messages_in_queue: bool ongoing_indicator: Optional[bool] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class GetVariablesPayload: get_variable_result: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class HeartbeatPayload: current_time: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class InstallCertificatePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class LogStatusNotificationPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class MeterValuesPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyChargingLimitPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyCustomerInformationPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyDisplayMessagesPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyEVChargingNeedsPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyEVChargingSchedulePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyEventPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyMonitoringReportPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class NotifyReportPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class PublishFirmwarePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class PublishFirmwareStatusNotificationPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class ReportChargingProfilesPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -256,91 +286,104 @@ class RequestStartTransactionPayload: status: str status_info: Optional[Dict] = None transaction_id: Optional[str] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class RequestStopTransactionPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ReservationStatusUpdatePayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class ReserveNowPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class ResetPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SecurityEventNotificationPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass class SendLocalListPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetChargingProfilePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetDisplayMessagePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetMonitoringBasePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetMonitoringLevelPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetNetworkProfilePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetVariableMonitoringPayload: set_monitoring_result: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class SetVariablesPayload: set_variable_result: List + custom_data: Optional[Dict[str, Any]] = None @dataclass class SignCertificatePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class StatusNotificationPayload: - pass + custom_data: Optional[Dict[str, Any]] = None @dataclass @@ -349,26 +392,31 @@ class TransactionEventPayload: charging_priority: Optional[int] = None id_token_info: Optional[Dict] = None updated_personal_message: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class TriggerMessagePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class UnlockConnectorPayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None @dataclass class UnpublishFirmwarePayload: status: str + custom_data: Optional[Dict[str, Any]] = None @dataclass class UpdateFirmwarePayload: status: str status_info: Optional[Dict] = None + custom_data: Optional[Dict[str, Any]] = None