diff --git a/custom_components/ocpp/manifest.json b/custom_components/ocpp/manifest.json index 4f77b7a3..bbe363bd 100644 --- a/custom_components/ocpp/manifest.json +++ b/custom_components/ocpp/manifest.json @@ -13,7 +13,7 @@ "iot_class": "local_push", "issue_tracker": "https://github.com/lbbrhzn/ocpp/issues", "requirements": [ - "ocpp==1.0.0", + "ocpp>=2.0.0", "websockets>=14.1" ], "version": "0.6.3" diff --git a/custom_components/ocpp/ocppv201.py b/custom_components/ocpp/ocppv201.py index 3114984f..e9e6063c 100644 --- a/custom_components/ocpp/ocppv201.py +++ b/custom_components/ocpp/ocppv201.py @@ -17,23 +17,24 @@ from ocpp.v201 import call, call_result from ocpp.v16.enums import ChargePointStatus as ChargePointStatusv16 from ocpp.v201.enums import ( - ConnectorStatusType, - GetVariableStatusType, - IdTokenType, - MeasurandType, - OperationalStatusType, - ResetType, - ResetStatusType, - SetVariableStatusType, - AuthorizationStatusType, - TransactionEventType, - ReadingContextType, - RequestStartStopStatusType, - ChargingStateType, - ChargingProfilePurposeType, - ChargingRateUnitType, - ChargingProfileKindType, - ChargingProfileStatus, + Action, + ConnectorStatusEnumType, + GetVariableStatusEnumType, + IdTokenEnumType, + MeasurandEnumType, + OperationalStatusEnumType, + ResetEnumType, + ResetStatusEnumType, + SetVariableStatusEnumType, + AuthorizationStatusEnumType, + TransactionEventEnumType, + ReadingContextEnumType, + RequestStartStopStatusEnumType, + ChargingStateEnumType, + ChargingProfilePurposeEnumType, + ChargingRateUnitEnumType, + ChargingProfileKindEnumType, + ChargingProfileStatusEnumType, ) from .chargepoint import ( @@ -71,7 +72,7 @@ class InventoryReport: smart_charging_available: bool = False reservation_available: bool = False local_auth_available: bool = False - tx_updated_measurands: list[MeasurandType] = [] + tx_updated_measurands: list[MeasurandEnumType] = [] class ChargePoint(cp): @@ -79,7 +80,7 @@ class ChargePoint(cp): _inventory: InventoryReport | None = None _wait_inventory: asyncio.Event | None = None - _connector_status: list[list[ConnectorStatusType | None]] = [] + _connector_status: list[list[ConnectorStatusEnumType | None]] = [] _tx_start_time: datetime | None = None def __init__( @@ -254,7 +255,7 @@ async def clear_profile(self): req: call.ClearChargingProfile = call.ClearChargingProfile( None, { - "charging_profile_Purpose": ChargingProfilePurposeType.charging_station_max_profile.value + "charging_profile_Purpose": ChargingProfilePurposeEnumType.charging_station_max_profile.value }, ) await self.call(req) @@ -275,10 +276,10 @@ async def set_charge_rate( schedule: dict = {"id": 1} if limit_amps < 32: period["limit"] = limit_amps - schedule["charging_rate_unit"] = ChargingRateUnitType.amps.value + schedule["charging_rate_unit"] = ChargingRateUnitEnumType.amps.value elif limit_watts < 22000: period["limit"] = limit_watts - schedule["charging_rate_unit"] = ChargingRateUnitType.watts.value + schedule["charging_rate_unit"] = ChargingRateUnitEnumType.watts.value else: await self.clear_profile() return @@ -289,14 +290,14 @@ async def set_charge_rate( { "id": 1, "stack_level": 0, - "charging_profile_purpose": ChargingProfilePurposeType.charging_station_max_profile, - "charging_profile_kind": ChargingProfileKindType.relative.value, + "charging_profile_purpose": ChargingProfilePurposeEnumType.charging_station_max_profile, + "charging_profile_kind": ChargingProfileKindEnumType.relative.value, "charging_schedule": [schedule], }, ) resp: call_result.SetChargingProfile = await self.call(req) - if resp.status != ChargingProfileStatus.accepted: + if resp.status != ChargingProfileStatusEnumType.accepted: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="set_variables_error", @@ -308,9 +309,9 @@ async def set_charge_rate( async def set_availability(self, state: bool = True): """Change availability.""" req: call.ChangeAvailability = call.ChangeAvailability( - OperationalStatusType.operative.value + OperationalStatusEnumType.operative.value if state - else OperationalStatusType.inoperative.value + else OperationalStatusEnumType.inoperative.value ) await self.call(req) @@ -319,12 +320,12 @@ async def start_transaction(self) -> bool: req: call.RequestStartTransaction = call.RequestStartTransaction( id_token={ "id_token": self._remote_id_tag, - "type": IdTokenType.central.value, + "type": IdTokenEnumType.central.value, }, remote_start_id=1, ) resp: call_result.RequestStartTransaction = await self.call(req) - return resp.status == RequestStartStopStatusType.accepted.value + return resp.status == RequestStartStopStatusEnumType.accepted.value async def stop_transaction(self) -> bool: """Request remote stop of current transaction.""" @@ -332,13 +333,13 @@ async def stop_transaction(self) -> bool: transaction_id=self._metrics[csess.transaction_id.value].value ) resp: call_result.RequestStopTransaction = await self.call(req) - return resp.status == RequestStartStopStatusType.accepted.value + return resp.status == RequestStartStopStatusEnumType.accepted.value async def reset(self, typ: str = ""): """Hard reset charger unless soft reset requested.""" - req: call.Reset = call.Reset(ResetType.immediate) + req: call.Reset = call.Reset(ResetEnumType.immediate) resp = await self.call(req) - if resp.status != ResetStatusType.accepted.value: + if resp.status != ResetStatusEnumType.accepted.value: status_suffix: str = f": {resp.status_info}" if resp.status_info else "" raise HomeAssistantError( translation_domain=DOMAIN, @@ -382,7 +383,7 @@ async def get_configuration(self, key: str = "") -> str | None: translation_placeholders={"message": str(e)}, ) result: dict = resp.get_variable_result[0] - if result["attribute_status"] != GetVariableStatusType.accepted: + if result["attribute_status"] != GetVariableStatusEnumType.accepted: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="get_variables_error", @@ -405,9 +406,9 @@ async def configure(self, key: str, value: str) -> SetVariableResult: translation_placeholders={"message": str(e)}, ) result: dict = resp.set_variable_result[0] - if result["attribute_status"] == SetVariableStatusType.accepted: + if result["attribute_status"] == SetVariableStatusEnumType.accepted: return SetVariableResult.accepted - elif result["attribute_status"] == SetVariableStatusType.reboot_required: + elif result["attribute_status"] == SetVariableStatusEnumType.reboot_required: return SetVariableResult.reboot_required else: raise HomeAssistantError( @@ -416,7 +417,7 @@ async def configure(self, key: str, value: str) -> SetVariableResult: translation_placeholders={"message": str(result)}, ) - @on("BootNotification") + @on(Action.boot_notification) def on_boot_notification(self, charging_station, reason, **kwargs): """Perform OCPP callback.""" resp = call_result.BootNotification( @@ -432,7 +433,7 @@ def on_boot_notification(self, charging_station, reason, **kwargs): self._register_boot_notification() return resp - @on("Heartbeat") + @on(Action.heartbeat) def on_heartbeat(self, **kwargs): """Perform OCPP callback.""" return call_result.Heartbeat(current_time=datetime.now(tz=UTC).isoformat()) @@ -448,7 +449,7 @@ def _report_evse_status(self, evse_id: int, evse_status_v16: ChargePointStatusv1 ) self.hass.async_create_task(self.update(self.central.cpid)) - @on("StatusNotification") + @on(Action.status_notification) def on_status_notification( self, timestamp: str, connector_status: str, evse_id: int, connector_id: int ): @@ -460,25 +461,25 @@ def on_status_notification( connector_id - len(self._connector_status[evse_id - 1]) ) - evse: list[ConnectorStatusType] = self._connector_status[evse_id - 1] - evse[connector_id - 1] = ConnectorStatusType(connector_status) - evse_status: ConnectorStatusType | None = None + evse: list[ConnectorStatusEnumType] = self._connector_status[evse_id - 1] + evse[connector_id - 1] = ConnectorStatusEnumType(connector_status) + evse_status: ConnectorStatusEnumType | None = None for status in evse: if status is None: evse_status = status break else: evse_status = status - if status != ConnectorStatusType.available: + if status != ConnectorStatusEnumType.available: break evse_status_v16: ChargePointStatusv16 | None if evse_status is None: evse_status_v16 = None - elif evse_status == ConnectorStatusType.available: + elif evse_status == ConnectorStatusEnumType.available: evse_status_v16 = ChargePointStatusv16.available - elif evse_status == ConnectorStatusType.faulted: + elif evse_status == ConnectorStatusEnumType.faulted: evse_status_v16 = ChargePointStatusv16.faulted - elif evse_status == ConnectorStatusType.unavailable: + elif evse_status == ConnectorStatusEnumType.unavailable: evse_status_v16 = ChargePointStatusv16.unavailable else: evse_status_v16 = ChargePointStatusv16.preparing @@ -488,15 +489,15 @@ def on_status_notification( return call_result.StatusNotification() - @on("FirmwareStatusNotification") - @on("MeterValues") - @on("LogStatusNotification") - @on("NotifyEvent") + @on(Action.firmware_status_notification) + @on(Action.meter_values) + @on(Action.log_status_notification) + @on(Action.notify_event) def ack(self, **kwargs): """Perform OCPP callback.""" return call_result.StatusNotification() - @on("NotifyReport") + @on(Action.notify_report) def on_report(self, request_id: int, generated_at: str, seq_no: int, **kwargs): """Perform OCPP callback.""" if self._wait_inventory is None: @@ -559,23 +560,23 @@ def on_report(self, request_id: int, generated_at: str, seq_no: int, **kwargs): characteristics: dict = report_data["variable_characteristics"] values: str = characteristics.get("values_list", "") self._inventory.tx_updated_measurands = [ - MeasurandType(s) for s in values.split(",") + MeasurandEnumType(s) for s in values.split(",") ] if not kwargs.get("tbc", False): self._wait_inventory.set() return call_result.NotifyReport() - @on("Authorize") + @on(Action.authorize) def on_authorize(self, id_token: dict, **kwargs): """Perform OCPP callback.""" - status: str = AuthorizationStatusType.unknown.value + status: str = AuthorizationStatusEnumType.unknown.value token_type: str = id_token["type"] token: str = id_token["id_token"] if ( - (token_type == IdTokenType.iso14443) - or (token_type == IdTokenType.iso15693) - or (token_type == IdTokenType.central) + (token_type == IdTokenEnumType.iso14443) + or (token_type == IdTokenEnumType.iso15693) + or (token_type == IdTokenEnumType.central) ): status = self.get_authorization_status(token) return call_result.Authorize(id_token_info={"status": status}) @@ -586,7 +587,7 @@ def _set_meter_values(self, tx_event_type: str, meter_values: list[dict]): measurands: list[MeasurandValue] = [] for sampled_value in meter_value["sampled_value"]: measurand: str = sampled_value.get( - "measurand", MeasurandType.energy_active_import_register.value + "measurand", MeasurandEnumType.energy_active_import_register.value ) value: float = sampled_value["value"] context: str = sampled_value.get("context", None) @@ -602,11 +603,11 @@ def _set_meter_values(self, tx_event_type: str, meter_values: list[dict]): ) converted_values.append(measurands) - if (tx_event_type == TransactionEventType.started.value) or ( - (tx_event_type == TransactionEventType.updated.value) + if (tx_event_type == TransactionEventEnumType.started.value) or ( + (tx_event_type == TransactionEventEnumType.updated.value) and (self._metrics[csess.meter_start].value is None) ): - energy_measurand = MeasurandType.energy_active_import_register.value + energy_measurand = MeasurandEnumType.energy_active_import_register.value for meter_value in converted_values: for measurand_item in meter_value: if measurand_item.measurand == energy_measurand: @@ -617,9 +618,9 @@ def _set_meter_values(self, tx_event_type: str, meter_values: list[dict]): self.process_measurands(converted_values, True) - if tx_event_type == TransactionEventType.ended.value: + if tx_event_type == TransactionEventEnumType.ended.value: measurands_in_tx: set[str] = set() - tx_end_context = ReadingContextType.transaction_end.value + tx_end_context = ReadingContextEnumType.transaction_end.value for meter_value in converted_values: for measurand_item in meter_value: if measurand_item.context == tx_end_context: @@ -633,9 +634,15 @@ def _set_meter_values(self, tx_event_type: str, meter_values: list[dict]): ): self._metrics[measurand].value = 0 - @on("TransactionEvent") + @on(Action.transaction_event) def on_transaction_event( - self, event_type, timestamp, trigger_reason, seq_no, transaction_info, **kwargs + self, + event_type, + timestamp, + trigger_reason, + seq_no, + transaction_info, + **kwargs, ): """Perform OCPP callback.""" offline: bool = kwargs.get("offline", False) @@ -647,15 +654,15 @@ def on_transaction_event( state = transaction_info["charging_state"] evse_id: int = kwargs["evse"]["id"] if "evse" in kwargs else 1 evse_status_v16: ChargePointStatusv16 | None = None - if state == ChargingStateType.idle: + if state == ChargingStateEnumType.idle: evse_status_v16 = ChargePointStatusv16.available - elif state == ChargingStateType.ev_connected: + elif state == ChargingStateEnumType.ev_connected: evse_status_v16 = ChargePointStatusv16.preparing - elif state == ChargingStateType.suspended_evse: + elif state == ChargingStateEnumType.suspended_evse: evse_status_v16 = ChargePointStatusv16.suspended_evse - elif state == ChargingStateType.suspended_ev: + elif state == ChargingStateEnumType.suspended_ev: evse_status_v16 = ChargePointStatusv16.suspended_ev - elif state == ChargingStateType.charging: + elif state == ChargingStateEnumType.charging: evse_status_v16 = ChargePointStatusv16.charging if evse_status_v16: self._report_evse_status(evse_id, evse_status_v16) @@ -663,11 +670,11 @@ def on_transaction_event( response = call_result.TransactionEvent() id_token = kwargs.get("id_token") if id_token: - response.id_token_info = {"status": AuthorizationStatusType.accepted} + response.id_token_info = {"status": AuthorizationStatusEnumType.accepted} id_tag_string: str = id_token["type"] + ":" + id_token["id_token"] self._metrics[cstat.id_tag.value].value = id_tag_string - if event_type == TransactionEventType.started.value: + if event_type == TransactionEventEnumType.started.value: self._tx_start_time = t tx_id: str = transaction_info["transaction_id"] self._metrics[csess.transaction_id.value].value = tx_id @@ -678,7 +685,7 @@ def on_transaction_event( duration_minutes: int = ((t - self._tx_start_time).seconds + 59) // 60 self._metrics[csess.session_time].value = duration_minutes self._metrics[csess.session_time].unit = UnitOfTime.MINUTES - if event_type == TransactionEventType.ended.value: + if event_type == TransactionEventEnumType.ended.value: self._metrics[csess.transaction_id.value].value = "" self._metrics[cstat.id_tag.value].value = "" diff --git a/requirements.txt b/requirements.txt index 8fc7e897..b6f8e8ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ colorlog==6.9.0 uv>=0.4 ruff==0.8.5 -ocpp==1.0.0 +ocpp==2.0.0 websockets==14.1 jsonschema==4.23.0 pre-commit==4.0.1 diff --git a/tests/test_charge_point_v201.py b/tests/test_charge_point_v201.py index cdea9634..b7073738 100644 --- a/tests/test_charge_point_v201.py +++ b/tests/test_charge_point_v201.py @@ -35,6 +35,7 @@ import pytest from pytest_homeassistant_custom_component.common import MockConfigEntry +import ocpp from ocpp.routing import on import ocpp.exceptions from ocpp.v201 import ChargePoint as cpclass, call, call_result @@ -50,38 +51,38 @@ ) from ocpp.v201.enums import ( Action, - AuthorizationStatusType, - BootReasonType, - ChangeAvailabilityStatusType, - ChargingProfileKindType, - ChargingProfilePurposeType, - ChargingProfileStatus, - ChargingRateUnitType, - ChargingStateType, - ClearChargingProfileStatusType, - ConnectorStatusType, - DataType, - FirmwareStatusType, - GenericDeviceModelStatusType, - GetVariableStatusType, - IdTokenType, - MeasurandType, - MutabilityType, - OperationalStatusType, - PhaseType, - ReadingContextType, - RegistrationStatusType, - ReportBaseType, - RequestStartStopStatusType, - ResetStatusType, - ResetType, - SetVariableStatusType, - ReasonType, - TransactionEventType, - MessageTriggerType, - TriggerMessageStatusType, - TriggerReasonType, - UpdateFirmwareStatusType, + AuthorizationStatusEnumType, + BootReasonEnumType, + ChangeAvailabilityStatusEnumType, + ChargingProfileKindEnumType, + ChargingProfilePurposeEnumType, + ChargingProfileStatusEnumType, + ChargingRateUnitEnumType, + ChargingStateEnumType, + ClearChargingProfileStatusEnumType, + ConnectorStatusEnumType, + DataEnumType, + FirmwareStatusEnumType, + GenericDeviceModelStatusEnumType, + GetVariableStatusEnumType, + IdTokenEnumType, + MeasurandEnumType, + MutabilityEnumType, + OperationalStatusEnumType, + PhaseEnumType, + ReadingContextEnumType, + RegistrationStatusEnumType, + ReportBaseEnumType, + RequestStartStopStatusEnumType, + ResetStatusEnumType, + ResetEnumType, + SetVariableStatusEnumType, + ReasonEnumType, + TransactionEventEnumType, + MessageTriggerEnumType, + TriggerMessageStatusEnumType, + TriggerReasonEnumType, + UpdateFirmwareStatusEnumType, ) from ocpp.v16.enums import ChargePointStatus as ChargePointStatusv16 @@ -111,13 +112,15 @@ class ChargePoint(cpclass): accept_reset: bool = True resets: list[call.Reset] = [] - @on(Action.GetBaseReport) + @on(Action.get_base_report) def _on_base_report(self, request_id: int, report_base: str, **kwargs): - assert report_base == ReportBaseType.full_inventory.value + assert report_base == ReportBaseEnumType.full_inventory.value self.task = asyncio.create_task(self._send_full_inventory(request_id)) - return call_result.GetBaseReport(GenericDeviceModelStatusType.accepted.value) + return call_result.GetBaseReport( + GenericDeviceModelStatusEnumType.accepted.value + ) - @on(Action.RequestStartTransaction) + @on(Action.request_start_transaction) def _on_remote_start( self, id_token: dict, remote_start_id: int, **kwargs ) -> call_result.RequestStartTransaction: @@ -128,18 +131,18 @@ def _on_remote_start( self._start_transaction_remote_start(id_token, remote_start_id) ) return call_result.RequestStartTransaction( - RequestStartStopStatusType.accepted.value + RequestStartStopStatusEnumType.accepted.value ) - @on(Action.RequestStopTransaction) + @on(Action.request_stop_transaction) def _on_remote_stop(self, transaction_id: str, **kwargs): assert transaction_id == self.remote_start_tx_id self.remote_stops.append(transaction_id) return call_result.RequestStopTransaction( - RequestStartStopStatusType.accepted.value + RequestStartStopStatusEnumType.accepted.value ) - @on(Action.SetVariables) + @on(Action.set_variables) def _on_set_variables(self, set_variable_data: list[dict], **kwargs): result: list[SetVariableResultType] = [] for input in set_variable_data: @@ -152,15 +155,15 @@ def _on_set_variables(self, set_variable_data: list[dict], **kwargs): ): self.tx_updated_measurands = input["attribute_value"].split(",") - attr_result: SetVariableStatusType + attr_result: SetVariableStatusEnumType if input["variable"] == {"name": "RebootRequired"}: - attr_result = SetVariableStatusType.reboot_required + attr_result = SetVariableStatusEnumType.reboot_required elif input["variable"] == {"name": "BadVariable"}: - attr_result = SetVariableStatusType.unknown_variable + attr_result = SetVariableStatusEnumType.unknown_variable elif input["variable"] == {"name": "VeryBadVariable"}: raise ocpp.exceptions.InternalError() else: - attr_result = SetVariableStatusType.accepted + attr_result = SetVariableStatusEnumType.accepted self.component_instance_used = input["component"].get("instance", None) self.variable_instance_used = input["variable"].get("instance", None) @@ -173,7 +176,7 @@ def _on_set_variables(self, set_variable_data: list[dict], **kwargs): ) return call_result.SetVariables(result) - @on(Action.GetVariables) + @on(Action.get_variables) def _on_get_variables(self, get_variable_data: list[dict], **kwargs): result: list[GetVariableResultType] = [] for input in get_variable_data: @@ -190,9 +193,9 @@ def _on_get_variables(self, get_variable_data: list[dict], **kwargs): raise ocpp.exceptions.InternalError() result.append( GetVariableResultType( - GetVariableStatusType.accepted + GetVariableStatusEnumType.accepted if value is not None - else GetVariableStatusType.unknown_variable, + else GetVariableStatusEnumType.unknown_variable, ComponentType(input["component"]["name"]), VariableType(input["variable"]["name"]), attribute_value=value, @@ -200,19 +203,19 @@ def _on_get_variables(self, get_variable_data: list[dict], **kwargs): ) return call_result.GetVariables(result) - @on(Action.ChangeAvailability) + @on(Action.change_availability) def _on_change_availability(self, operational_status: str, **kwargs): - if operational_status == OperationalStatusType.operative.value: + if operational_status == OperationalStatusEnumType.operative.value: self.operative = True - elif operational_status == OperationalStatusType.inoperative.value: + elif operational_status == OperationalStatusEnumType.inoperative.value: self.operative = False else: assert False return call_result.ChangeAvailability( - ChangeAvailabilityStatusType.accepted.value + ChangeAvailabilityStatusEnumType.accepted.value ) - @on(Action.SetChargingProfile) + @on(Action.set_charging_profile) def _on_set_charging_profile(self, evse_id: int, charging_profile: dict, **kwargs): self.charge_profiles_set.append( call.SetChargingProfile(evse_id, charging_profile) @@ -221,11 +224,15 @@ def _on_set_charging_profile(self, evse_id: int, charging_profile: dict, **kwarg limit = charging_profile["charging_schedule"][0]["charging_schedule_period"][0][ "limit" ] - if (unit == ChargingRateUnitType.amps.value) and (limit < 6): - return call_result.SetChargingProfile(ChargingProfileStatus.rejected.value) - return call_result.SetChargingProfile(ChargingProfileStatus.accepted.value) + if (unit == ChargingRateUnitEnumType.amps.value) and (limit < 6): + return call_result.SetChargingProfile( + ChargingProfileStatusEnumType.rejected.value + ) + return call_result.SetChargingProfile( + ChargingProfileStatusEnumType.accepted.value + ) - @on(Action.ClearChargingProfile) + @on(Action.clear_charging_profile) def _on_clear_charging_profile(self, **kwargs): self.charge_profiles_cleared.append( call.ClearChargingProfile( @@ -234,16 +241,16 @@ def _on_clear_charging_profile(self, **kwargs): ) ) return call_result.ClearChargingProfile( - ClearChargingProfileStatusType.accepted.value + ClearChargingProfileStatusEnumType.accepted.value ) - @on(Action.Reset) + @on(Action.reset) def _on_reset(self, type: str, **kwargs): self.resets.append(call.Reset(type, kwargs.get("evse_id", None))) return call_result.Reset( - ResetStatusType.accepted.value + ResetStatusEnumType.accepted.value if self.accept_reset - else ResetStatusType.rejected.value + else ResetStatusEnumType.rejected.value ) async def _start_transaction_remote_start( @@ -255,14 +262,14 @@ async def _start_transaction_remote_start( ) assert ( authorize_resp.id_token_info["status"] - == AuthorizationStatusType.accepted.value + == AuthorizationStatusEnumType.accepted.value ) self.tx_start_time = datetime.now(tz=UTC) request = call.TransactionEvent( - TransactionEventType.started.value, + TransactionEventEnumType.started.value, self.tx_start_time.isoformat(), - TriggerReasonType.remote_start.value, + TriggerReasonEnumType.remote_start.value, 0, transaction_info={ "transaction_id": self.remote_start_tx_id, @@ -297,7 +304,7 @@ async def _send_full_inventory(self, request_id: int): VariableType("Available"), [ VariableAttributeType( - value="true", mutability=MutabilityType.read_only + value="true", mutability=MutabilityEnumType.read_only ) ], ) @@ -316,7 +323,7 @@ async def _send_full_inventory(self, request_id: int): VariableType("Available"), [ VariableAttributeType( - value="true", mutability=MutabilityType.read_only + value="true", mutability=MutabilityEnumType.read_only ) ], ), @@ -335,7 +342,7 @@ async def _send_full_inventory(self, request_id: int): VariableType("Available"), [ VariableAttributeType( - value="true", mutability=MutabilityType.read_only + value="true", mutability=MutabilityEnumType.read_only ) ], ), @@ -354,7 +361,7 @@ async def _send_full_inventory(self, request_id: int): VariableType("Available"), [ VariableAttributeType( - value="true", mutability=MutabilityType.read_only + value="true", mutability=MutabilityEnumType.read_only ) ], ), @@ -373,7 +380,7 @@ async def _send_full_inventory(self, request_id: int): VariableType("Available"), [ VariableAttributeType( - value="true", mutability=MutabilityType.read_only + value="true", mutability=MutabilityEnumType.read_only ) ], ), @@ -392,7 +399,7 @@ async def _send_full_inventory(self, request_id: int): VariableType("TxUpdatedMeasurands"), [VariableAttributeType(value="", persistent=True)], VariableCharacteristicsType( - DataType.member_list, + DataEnumType.member_list, False, values_list=",".join(supported_measurands), ), @@ -409,7 +416,7 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo assert len(cp.remote_starts) == 1 assert cp.remote_starts[0].id_token == { "id_token": cs.charge_points[cpid]._remote_id_tag, - "type": IdTokenType.central.value, + "type": IdTokenEnumType.central.value, } while cs.get_metric(cpid, csess.transaction_id.value) is None: await asyncio.sleep(0.1) @@ -418,7 +425,7 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo tx_start_time = cp.tx_start_time await cp.call( call.StatusNotification( - tx_start_time.isoformat(), ConnectorStatusType.occupied, 1, 1 + tx_start_time.isoformat(), ConnectorStatusEnumType.occupied, 1, 1 ) ) assert ( @@ -428,9 +435,9 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo await cp.call( call.TransactionEvent( - TransactionEventType.updated.value, + TransactionEventEnumType.updated.value, tx_start_time.isoformat(), - TriggerReasonType.cable_plugged_in.value, + TriggerReasonEnumType.cable_plugged_in.value, 1, transaction_info={ "transaction_id": cp.remote_start_tx_id, @@ -439,13 +446,13 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo ) await cp.call( call.TransactionEvent( - TransactionEventType.updated.value, + TransactionEventEnumType.updated.value, tx_start_time.isoformat(), - TriggerReasonType.charging_state_changed.value, + TriggerReasonEnumType.charging_state_changed.value, 2, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.charging.value, + "charging_state": ChargingStateEnumType.charging.value, }, meter_value=[ { @@ -454,55 +461,55 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo { "value": 0, "measurand": Measurand.current_export.value, - "phase": PhaseType.l1.value, + "phase": PhaseEnumType.l1.value, "unit_of_measure": {"unit": "A"}, }, { "value": 0, "measurand": Measurand.current_export.value, - "phase": PhaseType.l2.value, + "phase": PhaseEnumType.l2.value, "unit_of_measure": {"unit": "A"}, }, { "value": 0, "measurand": Measurand.current_export.value, - "phase": PhaseType.l3.value, + "phase": PhaseEnumType.l3.value, "unit_of_measure": {"unit": "A"}, }, { "value": 1.1, "measurand": Measurand.current_import.value, - "phase": PhaseType.l1.value, + "phase": PhaseEnumType.l1.value, "unit_of_measure": {"unit": "A"}, }, { "value": 2.2, "measurand": Measurand.current_import.value, - "phase": PhaseType.l2.value, + "phase": PhaseEnumType.l2.value, "unit_of_measure": {"unit": "A"}, }, { "value": 3.3, "measurand": Measurand.current_import.value, - "phase": PhaseType.l3.value, + "phase": PhaseEnumType.l3.value, "unit_of_measure": {"unit": "A"}, }, { "value": 12.1, "measurand": Measurand.current_offered.value, - "phase": PhaseType.l1.value, + "phase": PhaseEnumType.l1.value, "unit_of_measure": {"unit": "A"}, }, { "value": 12.2, "measurand": Measurand.current_offered.value, - "phase": PhaseType.l2.value, + "phase": PhaseEnumType.l2.value, "unit_of_measure": {"unit": "A"}, }, { "value": 12.3, "measurand": Measurand.current_offered.value, - "phase": PhaseType.l3.value, + "phase": PhaseEnumType.l3.value, "unit_of_measure": {"unit": "A"}, }, { @@ -567,25 +574,25 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo { "value": 229.9, "measurand": Measurand.voltage.value, - "phase": PhaseType.l1_n.value, + "phase": PhaseEnumType.l1_n.value, "unit_of_measure": {"unit": "V"}, }, { "value": 230, "measurand": Measurand.voltage.value, - "phase": PhaseType.l2_n.value, + "phase": PhaseEnumType.l2_n.value, "unit_of_measure": {"unit": "V"}, }, { "value": 230.4, "measurand": Measurand.voltage.value, - "phase": PhaseType.l3_n.value, + "phase": PhaseEnumType.l3_n.value, "unit_of_measure": {"unit": "V"}, }, { # Not among enabled measurands, will be ignored "value": 1111, - "measurand": MeasurandType.energy_active_net.value, + "measurand": MeasurandEnumType.energy_active_net.value, "unit_of_measure": {"unit": "Wh"}, }, ], @@ -617,13 +624,13 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo await cp.call( call.TransactionEvent( - TransactionEventType.updated.value, + TransactionEventEnumType.updated.value, (tx_start_time + timedelta(seconds=60)).isoformat(), - TriggerReasonType.meter_value_periodic.value, + TriggerReasonEnumType.meter_value_periodic.value, 3, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.charging.value, + "charging_state": ChargingStateEnumType.charging.value, }, meter_value=[ { @@ -647,14 +654,14 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo await cp.call( call.TransactionEvent( - TransactionEventType.ended.value, + TransactionEventEnumType.ended.value, (tx_start_time + timedelta(seconds=120)).isoformat(), - TriggerReasonType.remote_stop.value, + TriggerReasonEnumType.remote_stop.value, 4, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.ev_connected.value, - "stopped_reason": ReasonType.remote.value, + "charging_state": ChargingStateEnumType.ev_connected.value, + "stopped_reason": ReasonEnumType.remote.value, }, meter_value=[ { @@ -662,7 +669,7 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo "sampled_value": [ { "value": 333, - "context": ReadingContextType.transaction_end, + "context": ReadingContextEnumType.transaction_end, "measurand": Measurand.energy_active_import_register.value, "unit_of_measure": {"unit": "Wh"}, }, @@ -686,13 +693,13 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo # Now with energy reading in Started transaction event await cp.call( call.TransactionEvent( - TransactionEventType.started.value, + TransactionEventEnumType.started.value, tx_start_time.isoformat(), - TriggerReasonType.cable_plugged_in.value, + TriggerReasonEnumType.cable_plugged_in.value, 0, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.ev_connected.value, + "charging_state": ChargingStateEnumType.ev_connected.value, }, meter_value=[ { @@ -714,13 +721,13 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo ) await cp.call( call.TransactionEvent( - TransactionEventType.updated.value, + TransactionEventEnumType.updated.value, tx_start_time.isoformat(), - TriggerReasonType.charging_state_changed.value, + TriggerReasonEnumType.charging_state_changed.value, 1, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.charging.value, + "charging_state": ChargingStateEnumType.charging.value, }, meter_value=[ { @@ -740,13 +747,13 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo await cp.call( call.TransactionEvent( - TransactionEventType.updated.value, + TransactionEventEnumType.updated.value, tx_start_time.isoformat(), - TriggerReasonType.charging_state_changed.value, + TriggerReasonEnumType.charging_state_changed.value, 1, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.suspended_ev.value, + "charging_state": ChargingStateEnumType.suspended_ev.value, }, ) ) @@ -757,13 +764,13 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo await cp.call( call.TransactionEvent( - TransactionEventType.updated.value, + TransactionEventEnumType.updated.value, tx_start_time.isoformat(), - TriggerReasonType.charging_state_changed.value, + TriggerReasonEnumType.charging_state_changed.value, 1, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.suspended_evse.value, + "charging_state": ChargingStateEnumType.suspended_evse.value, }, ) ) @@ -774,14 +781,14 @@ async def _test_transaction(hass: HomeAssistant, cs: CentralSystem, cp: ChargePo await cp.call( call.TransactionEvent( - TransactionEventType.ended.value, + TransactionEventEnumType.ended.value, tx_start_time.isoformat(), - TriggerReasonType.ev_communication_lost.value, + TriggerReasonEnumType.ev_communication_lost.value, 2, transaction_info={ "transaction_id": cp.remote_start_tx_id, - "charging_state": ChargingStateType.idle.value, - "stopped_reason": ReasonType.ev_disconnected.value, + "charging_state": ChargingStateEnumType.idle.value, + "stopped_reason": ReasonEnumType.ev_disconnected.value, }, ) ) @@ -917,13 +924,13 @@ async def _test_charge_profiles( assert cp.charge_profiles_set[-1].charging_profile == { "id": 1, "stack_level": 0, - "charging_profile_purpose": ChargingProfilePurposeType.charging_station_max_profile, - "charging_profile_kind": ChargingProfileKindType.relative.value, + "charging_profile_purpose": ChargingProfilePurposeEnumType.charging_station_max_profile, + "charging_profile_kind": ChargingProfileKindEnumType.relative.value, "charging_schedule": [ { "id": 1, "charging_schedule_period": [{"start_period": 0, "limit": 3000}], - "charging_rate_unit": ChargingRateUnitType.watts.value, + "charging_rate_unit": ChargingRateUnitEnumType.watts.value, }, ], } @@ -935,13 +942,13 @@ async def _test_charge_profiles( assert cp.charge_profiles_set[-1].charging_profile == { "id": 1, "stack_level": 0, - "charging_profile_purpose": ChargingProfilePurposeType.charging_station_max_profile, - "charging_profile_kind": ChargingProfileKindType.relative.value, + "charging_profile_purpose": ChargingProfilePurposeEnumType.charging_station_max_profile, + "charging_profile_kind": ChargingProfileKindEnumType.relative.value, "charging_schedule": [ { "id": 1, "charging_schedule_period": [{"start_period": 0, "limit": 16}], - "charging_rate_unit": ChargingRateUnitType.amps.value, + "charging_rate_unit": ChargingRateUnitEnumType.amps.value, }, ], } @@ -968,13 +975,13 @@ async def _test_charge_profiles( assert cp.charge_profiles_set[-1].charging_profile == { "id": 2, "stack_level": 1, - "charging_profile_purpose": ChargingProfilePurposeType.tx_profile.value, - "charging_profile_kind": ChargingProfileKindType.relative.value, + "charging_profile_purpose": ChargingProfilePurposeEnumType.tx_profile.value, + "charging_profile_kind": ChargingProfileKindEnumType.relative.value, "charging_schedule": [ { "id": 1, "charging_schedule_period": [{"start_period": 0, "limit": 6}], - "charging_rate_unit": ChargingRateUnitType.amps.value, + "charging_rate_unit": ChargingRateUnitEnumType.amps.value, }, ], } @@ -985,13 +992,13 @@ async def _test_charge_profiles( assert cp.charge_profiles_set[-1].charging_profile == { "id": 1, "stack_level": 0, - "charging_profile_purpose": ChargingProfilePurposeType.charging_station_max_profile.value, - "charging_profile_kind": ChargingProfileKindType.relative.value, + "charging_profile_purpose": ChargingProfilePurposeEnumType.charging_station_max_profile.value, + "charging_profile_kind": ChargingProfileKindEnumType.relative.value, "charging_schedule": [ { "id": 1, "charging_schedule_period": [{"start_period": 0, "limit": 12}], - "charging_rate_unit": ChargingRateUnitType.amps.value, + "charging_rate_unit": ChargingRateUnitEnumType.amps.value, }, ], } @@ -1005,7 +1012,7 @@ async def _test_charge_profiles( assert len(cp.charge_profiles_cleared) == 1 assert cp.charge_profiles_cleared[-1].charging_profile_id is None assert cp.charge_profiles_cleared[-1].charging_profile_criteria == { - "charging_profile_purpose": ChargingProfilePurposeType.charging_station_max_profile.value + "charging_profile_purpose": ChargingProfilePurposeEnumType.charging_station_max_profile.value } @@ -1018,15 +1025,15 @@ async def _run_test(hass: HomeAssistant, cs: CentralSystem, cp: ChargePoint): "vendor_name": "VENDOR", "firmware_version": "VERSION", }, - BootReasonType.power_up.value, + BootReasonEnumType.power_up.value, ) ) - assert boot_res.status == RegistrationStatusType.accepted.value + assert boot_res.status == RegistrationStatusEnumType.accepted.value assert boot_res.status_info is None datetime.fromisoformat(boot_res.current_time) await cp.call( call.StatusNotification( - datetime.now(tz=UTC).isoformat(), ConnectorStatusType.available, 1, 1 + datetime.now(tz=UTC).isoformat(), ConnectorStatusEnumType.available, 1, 1 ) ) @@ -1049,7 +1056,7 @@ async def _run_test(hass: HomeAssistant, cs: CentralSystem, cp: ChargePoint): ) assert ( cs.get_metric(cpid, cstat.status_connector.value) - == ConnectorStatusType.available.value + == ConnectorStatusEnumType.available.value ) assert cp.tx_updated_interval == DEFAULT_METER_INTERVAL assert cp.tx_updated_measurands == supported_measurands @@ -1064,7 +1071,7 @@ async def _run_test(hass: HomeAssistant, cs: CentralSystem, cp: ChargePoint): await press_button(hass, cs, "reset") assert len(cp.resets) == 1 - assert cp.resets[0].type == ResetType.immediate.value + assert cp.resets[0].type == ResetEnumType.immediate.value assert cp.resets[0].evse_id is None error: HomeAssistantError = None @@ -1080,25 +1087,27 @@ async def _run_test(hass: HomeAssistant, cs: CentralSystem, cp: ChargePoint): assert not cp.operative await cp.call( call.StatusNotification( - datetime.now(tz=UTC).isoformat(), ConnectorStatusType.unavailable, 1, 1 + datetime.now(tz=UTC).isoformat(), ConnectorStatusEnumType.unavailable, 1, 1 ) ) assert ( cs.get_metric(cpid, cstat.status_connector.value) - == ConnectorStatusType.unavailable.value + == ConnectorStatusEnumType.unavailable.value ) await cp.call( call.StatusNotification( - datetime.now(tz=UTC).isoformat(), ConnectorStatusType.faulted, 1, 1 + datetime.now(tz=UTC).isoformat(), ConnectorStatusEnumType.faulted, 1, 1 ) ) assert ( cs.get_metric(cpid, cstat.status_connector.value) - == ConnectorStatusType.faulted.value + == ConnectorStatusEnumType.faulted.value ) - await cp.call(call.FirmwareStatusNotification(FirmwareStatusType.installed.value)) + await cp.call( + call.FirmwareStatusNotification(FirmwareStatusEnumType.installed.value) + ) class ChargePointAllFeatures(ChargePoint): @@ -1106,19 +1115,19 @@ class ChargePointAllFeatures(ChargePoint): triggered_status_notification: list[EVSEType] = [] - @on(Action.UpdateFirmware) + @on(Action.update_firmware) def _on_update_firmware(self, request_id: int, firmware: dict, **kwargs): - return call_result.UpdateFirmware(UpdateFirmwareStatusType.rejected.value) + return call_result.UpdateFirmware(UpdateFirmwareStatusEnumType.rejected.value) - @on(Action.TriggerMessage) + @on(Action.trigger_message) def _on_trigger_message(self, requested_message: str, **kwargs): - if (requested_message == MessageTriggerType.status_notification) and ( + if (requested_message == MessageTriggerEnumType.status_notification) and ( "evse" in kwargs ): self.triggered_status_notification.append( EVSEType(kwargs["evse"]["id"], kwargs["evse"]["connector_id"]) ) - return call_result.TriggerMessage(TriggerMessageStatusType.rejected.value) + return call_result.TriggerMessage(TriggerMessageStatusEnumType.rejected.value) async def _extra_features_test( @@ -1134,7 +1143,7 @@ async def _extra_features_test( "vendor_name": "VENDOR", "firmware_version": "VERSION", }, - BootReasonType.power_up.value, + BootReasonEnumType.power_up.value, ) ) await wait_ready(hass) @@ -1158,7 +1167,7 @@ async def _extra_features_test( class ChargePointReportUnsupported(ChargePointAllFeatures): """A charge point which does not support GetBaseReport.""" - @on(Action.GetBaseReport) + @on(Action.get_base_report) def _on_base_report(self, request_id: int, report_base: str, **kwargs): raise ocpp.exceptions.NotImplementedError("This is not implemented") @@ -1166,7 +1175,7 @@ def _on_base_report(self, request_id: int, report_base: str, **kwargs): class ChargePointReportFailing(ChargePointAllFeatures): """A charge point which keeps failing GetBaseReport.""" - @on(Action.GetBaseReport) + @on(Action.get_base_report) def _on_base_report(self, request_id: int, report_base: str, **kwargs): raise ocpp.exceptions.InternalError("Test failure") @@ -1184,7 +1193,7 @@ async def _unsupported_base_report_test( "vendor_name": "VENDOR", "firmware_version": "VERSION", }, - BootReasonType.power_up.value, + BootReasonEnumType.power_up.value, ) ) await wait_ready(hass) @@ -1210,6 +1219,8 @@ async def test_cms_responses_v201(hass, socket_enabled): domain=OCPP_DOMAIN, data=config_data, entry_id="test_cms", title="test_cms" ) cs: CentralSystem = await create_configuration(hass, config_entry) + # threading in async validation causes tests to fail + ocpp.messages.ASYNC_VALIDATION = False await run_charge_point_test( config_entry, "CP_2",