├── src └── main │ ├── resources │ └── application.properties │ └── java │ └── com │ └── extrawest │ └── ocpi │ ├── model │ ├── markers │ │ ├── OcpiRequestData.java │ │ ├── LocationData.java │ │ └── OcpiResponseData.java │ ├── dto │ │ ├── AbstractDomainObject.java │ │ ├── charging_profile │ │ │ ├── results │ │ │ │ ├── AbstractProfileResult.java │ │ │ │ ├── ClearProfileResult.java │ │ │ │ ├── ChargingProfileResult.java │ │ │ │ └── ActiveChargingProfileResult.java │ │ │ ├── ActiveChargingProfile.java │ │ │ ├── ChargingProfilePeriod.java │ │ │ ├── SetChargingProfile.java │ │ │ ├── ChargingProfileResponse.java │ │ │ └── ChargingProfile.java │ │ ├── command │ │ │ ├── StopSession.java │ │ │ ├── AbstractCommand.java │ │ │ ├── CommandResult.java │ │ │ ├── CommandResponse.java │ │ │ ├── CancelReservation.java │ │ │ ├── UnlockConnector.java │ │ │ ├── StartSession.java │ │ │ └── ReserveNow.java │ │ ├── VersionDto.java │ │ ├── tariff │ │ │ ├── TariffElement.java │ │ │ ├── PriceComponent.java │ │ │ └── TariffDto.java │ │ ├── DisplayText.java │ │ ├── VersionDetailsDto.java │ │ ├── location │ │ │ ├── ExceptionalPeriod.java │ │ │ ├── EnvironmentalImpact.java │ │ │ ├── EnergySource.java │ │ │ ├── RegularHours.java │ │ │ ├── GeoLocation.java │ │ │ ├── EnergyMix.java │ │ │ ├── StatusSchedule.java │ │ │ ├── AdditionalGeoLocation.java │ │ │ ├── Image.java │ │ │ ├── PublishTokenType.java │ │ │ ├── Hours.java │ │ │ ├── Connector.java │ │ │ └── EVSE.java │ │ ├── Price.java │ │ ├── CdrDimension.java │ │ ├── BusinessDetails.java │ │ ├── Endpoint.java │ │ ├── PaginationHeaders.java │ │ ├── CredentialsDto.java │ │ ├── token │ │ │ └── EnergyContract.java │ │ ├── LocationReferencesDto.java │ │ ├── CredentialsRole.java │ │ ├── cdr │ │ │ ├── SignedValue.java │ │ │ ├── SignedData.java │ │ │ ├── CdrToken.java │ │ │ └── CdrLocation.java │ │ ├── ClientOwnedObject.java │ │ ├── ClientInfoDto.java │ │ ├── ChargingPreferences.java │ │ ├── ChargingPeriod.java │ │ ├── AuthorizationInfoDto.java │ │ ├── ResponseFormat.java │ │ └── SessionDto.java │ ├── enums │ │ ├── DayOfWeek.java │ │ ├── Status.java │ │ ├── ConnectorFormat.java │ │ ├── EnvironmentalImpactCategory.java │ │ ├── InterfaceRole.java │ │ ├── ConnectionStatus.java │ │ ├── ReservationRestrictionType.java │ │ ├── PowerType.java │ │ ├── ModuleID.java │ │ ├── ChargingProfileResultType.java │ │ ├── TokenType.java │ │ ├── VersionNumber.java │ │ ├── AllowedType.java │ │ ├── ProfileType.java │ │ ├── TariffDimensionType.java │ │ ├── Role.java │ │ ├── CommandResponseType.java │ │ ├── ParkingType.java │ │ ├── AuthMethod.java │ │ ├── ParkingRestriction.java │ │ ├── EnergySourceCategory.java │ │ ├── CommandType.java │ │ ├── TariffType.java │ │ ├── ChargingProfileResponseType.java │ │ ├── ChargingRateUnit.java │ │ ├── WhitelistType.java │ │ ├── ChargingPreferencesResponse.java │ │ ├── SessionStatus.java │ │ ├── ImageCategory.java │ │ ├── CommandResultType.java │ │ ├── status_codes │ │ │ └── OcpiStatusCode.java │ │ ├── Facility.java │ │ ├── Capability.java │ │ └── CdrDimensionType.java │ └── security │ │ ├── Permission.java │ │ └── Role.java │ ├── util │ ├── Constants.java │ └── EnumUtil.java │ ├── service │ ├── ClientInfoService.java │ ├── pagination │ │ ├── PaginationService.java │ │ └── PaginationServiceImpl.java │ ├── CpoCommandsService.java │ ├── CpoCdrService.java │ ├── CpoTariffService.java │ ├── CpoCredentialsService.java │ ├── CpoVersionService.java │ ├── HubClientInfoService.java │ ├── CpoTokensService.java │ ├── CpoSessionsService.java │ ├── CpoChargingProfilesService.java │ └── CpoLocationService.java │ ├── exception │ ├── OcpiInvalidParametersException.java │ ├── OcpiResourceNotFoundException.java │ ├── OcpiUnknownTokenException.java │ ├── NotEnoughInformationException.java │ ├── OcpiGeneralClientException.java │ ├── PropertyConstraintException.java │ └── handlers │ │ └── OcpiExceptionHandler.java │ ├── config │ ├── ObjectMapperConfig.java │ └── OpenAPIConfig.java │ ├── validation │ └── ClientObjectValidation.java │ └── controller │ ├── ClientInfoController.java │ ├── CpoCommandsController.java │ ├── CpoVersionController.java │ ├── CpoCdrController.java │ ├── CpoCredentialsController.java │ ├── CpoTariffController.java │ ├── HubClientInfoController.java │ ├── CpoSessionsController.java │ └── CpoChargingProfilesController.java ├── .github └── workflows │ ├── github-release.yml │ ├── snapshot-publish.yml │ └── release-publish.yml ├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties └── LICENSE /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8090 2 | extrawest.openapi.dev-url=http://localhost:8080 3 | extrawest.openapi.prod-url=https://extrawest-api.com -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/markers/OcpiRequestData.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.markers; 2 | 3 | /* 4 | * An interface to mark a Request Data 5 | */ 6 | public interface OcpiRequestData { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/util/Constants.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.util; 2 | 3 | public final class Constants { 4 | 5 | public static final String ASCII_REGEXP = "/[:ascii:]/i"; 6 | 7 | private Constants() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/markers/LocationData.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.markers; 2 | 3 | /* 4 | * An interface to mark a location data. Can be Location, EVSE or Connector 5 | */ 6 | public interface LocationData extends OcpiRequestData, OcpiResponseData { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/markers/OcpiResponseData.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.markers; 2 | 3 | /* 4 | * An interface to mark a Response Data, that need to be wrapped into {@link com.extrawest.ocpi.model.dto.ResponseFormat} 5 | */ 6 | public interface OcpiResponseData { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/AbstractDomainObject.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | public abstract class AbstractDomainObject { 6 | @JsonIgnore 7 | public String type; 8 | 9 | public String getType() { 10 | return this.getClass().getSimpleName(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/ClientInfoService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | 4 | import com.extrawest.ocpi.model.dto.ClientInfoDto; 5 | 6 | public interface ClientInfoService { 7 | 8 | ClientInfoDto getHubClientInfo(String countryCode, String partyId); 9 | 10 | void putHubClientInfo(String countryCode, String partyId); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/github-release.yml: -------------------------------------------------------------------------------- 1 | name: Create a GitHub Release with release details specified 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release: 10 | name: Github Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: marvinpinto/action-automatic-releases@latest 14 | with: 15 | repo_token: ${{ secrets.GITHUB_TOKEN }} 16 | prerelease: false -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/pagination/PaginationService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service.pagination; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import org.springframework.http.HttpHeaders; 5 | 6 | public interface PaginationService { 7 | int adjustLimitByMax(Integer limit); 8 | 9 | HttpHeaders buildHeader(Integer offset, Integer limit, HttpServletRequest request, long totalCount); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoCommandsService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.command.AbstractCommand; 4 | import com.extrawest.ocpi.model.dto.command.CommandResponse; 5 | import com.extrawest.ocpi.model.enums.CommandType; 6 | 7 | public interface CpoCommandsService { 8 | 9 | CommandResponse postCommand(CommandType command, AbstractCommand requestedCommand); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/exception/OcpiInvalidParametersException.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.exception; 2 | 3 | import org.apache.logging.log4j.util.Strings; 4 | 5 | import java.util.Arrays; 6 | 7 | public class OcpiInvalidParametersException extends RuntimeException { 8 | 9 | public OcpiInvalidParametersException(String... reasons) { 10 | super(reasons.length == 0 ? Strings.EMPTY : Arrays.toString(reasons)); 11 | } 12 | } -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/exception/OcpiResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.exception; 2 | 3 | import org.apache.logging.log4j.util.Strings; 4 | 5 | import java.util.Arrays; 6 | 7 | 8 | public class OcpiResourceNotFoundException extends RuntimeException { 9 | 10 | public OcpiResourceNotFoundException(String... reasons) { 11 | super(reasons.length == 0 ? Strings.EMPTY : Arrays.toString(reasons)); 12 | } 13 | } -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoCdrService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | 4 | import com.extrawest.ocpi.model.dto.cdr.CDRDto; 5 | 6 | import java.time.LocalDateTime; 7 | import java.util.List; 8 | 9 | public interface CpoCdrService { 10 | List getCdr(LocalDateTime dateFrom, LocalDateTime dateTo, Integer offset, Integer limit); 11 | 12 | long getTotalCount(LocalDateTime dateFrom, LocalDateTime dateTo); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoTariffService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.tariff.TariffDto; 4 | 5 | import java.time.LocalDateTime; 6 | import java.util.List; 7 | 8 | public interface CpoTariffService { 9 | List getAll(LocalDateTime dateFrom, LocalDateTime dateTo, Integer offset, Integer limit); 10 | 11 | long getTotalCount(LocalDateTime dateFrom, LocalDateTime dateTo); 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/timing.properties 11 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 12 | .mvn/wrapper/maven-wrapper.jar 13 | 14 | # Eclipse m2e generated files 15 | # Eclipse Core 16 | .project 17 | # JDT-specific (Eclipse Java Development Tools) 18 | .classpath 19 | .sh -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoCredentialsService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.CredentialsDto; 4 | 5 | public interface CpoCredentialsService { 6 | 7 | CredentialsDto getCredentials(); 8 | 9 | CredentialsDto postCredentials(CredentialsDto credentialsDTO); 10 | 11 | void putCredentials(CredentialsDto credentialsDTO); 12 | 13 | void deleteCredentials(CredentialsDto credentialsDTO); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoVersionService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | 4 | import com.extrawest.ocpi.model.dto.VersionDetailsDto; 5 | import com.extrawest.ocpi.model.dto.VersionDto; 6 | import com.extrawest.ocpi.model.enums.VersionNumber; 7 | 8 | import java.util.List; 9 | 10 | public interface CpoVersionService { 11 | 12 | List getVersions(); 13 | 14 | VersionDetailsDto getVersionDetails(VersionNumber version); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/HubClientInfoService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.ClientInfoDto; 4 | 5 | import java.time.LocalDateTime; 6 | import java.util.List; 7 | 8 | public interface HubClientInfoService { 9 | 10 | List getClientInfoList(LocalDateTime dateFrom, LocalDateTime dateTo, Integer offset, Integer limit); 11 | 12 | long getTotalCount(LocalDateTime dateFrom, LocalDateTime dateTo); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/exception/OcpiUnknownTokenException.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.exception; 2 | 3 | import org.apache.logging.log4j.util.Strings; 4 | 5 | import java.util.Arrays; 6 | 7 | public class OcpiUnknownTokenException extends RuntimeException { 8 | public OcpiUnknownTokenException(String... reasons) { 9 | super(getErrorLogMsg(reasons)); 10 | } 11 | 12 | private static String getErrorLogMsg(String... reasons) { 13 | return reasons.length == 0 ? Strings.EMPTY : Arrays.toString(reasons); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/exception/NotEnoughInformationException.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.exception; 2 | 3 | import org.apache.logging.log4j.util.Strings; 4 | 5 | import java.util.Arrays; 6 | 7 | public class NotEnoughInformationException extends RuntimeException { 8 | 9 | public NotEnoughInformationException(String... reasons) { 10 | super(getErrorLogMsg(reasons)); 11 | } 12 | 13 | private static String getErrorLogMsg(String... reasons) { 14 | return reasons.length == 0 ? Strings.EMPTY : Arrays.toString(reasons); 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoTokensService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.token.TokenDto; 4 | import com.extrawest.ocpi.model.enums.TokenType; 5 | 6 | public interface CpoTokensService { 7 | 8 | TokenDto getToken(String countryCode, String partyId, String tokenUid, TokenType type); 9 | 10 | TokenDto putToken(TokenDto tokenDTO, String countryCode, String partyId, String tokenUid, TokenType type); 11 | 12 | TokenDto patchToken(TokenDto tokenDTO, String countryCode, String partyId, String tokenUid, TokenType type); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/exception/OcpiGeneralClientException.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.exception; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.logging.log4j.util.Strings; 5 | 6 | import java.util.Arrays; 7 | 8 | @Slf4j 9 | public class OcpiGeneralClientException extends RuntimeException { 10 | 11 | public OcpiGeneralClientException(String... reasons) { 12 | super(getErrorLogMsg(reasons)); 13 | } 14 | 15 | private static String getErrorLogMsg(String... reasons) { 16 | return reasons.length == 0 ? Strings.EMPTY : Arrays.toString(reasons); 17 | } 18 | } -------------------------------------------------------------------------------- /.github/workflows/snapshot-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Snapshot to GitHub Packages 2 | 3 | on: 4 | push: 5 | branches: 6 | - RC-2023* 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-java@v3 17 | with: 18 | java-version: '17' 19 | distribution: 'adopt' 20 | - name: Publish package 21 | run: mvn --batch-mode deploy -DrepositoryId=github 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/results/AbstractProfileResult.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile.results; 2 | 3 | import com.extrawest.ocpi.model.enums.ChargingProfileResultType; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | public abstract class AbstractProfileResult { 8 | 9 | @JsonIgnore 10 | public String type; 11 | @JsonProperty("result") 12 | protected ChargingProfileResultType result; 13 | 14 | public String getType() { 15 | return this.getClass().getSimpleName(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoSessionsService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.ChargingPreferences; 4 | import com.extrawest.ocpi.model.dto.SessionDto; 5 | 6 | import java.time.LocalDateTime; 7 | import java.util.List; 8 | 9 | public interface CpoSessionsService { 10 | 11 | List getSessions(LocalDateTime dateFrom, LocalDateTime dateTo, Integer offset, Integer limit); 12 | 13 | long getTotalCount(LocalDateTime dateFrom, LocalDateTime dateTo); 14 | 15 | ChargingPreferences putChargingPreferences(String sessionId, ChargingPreferences chargingPreferencesDTO); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/results/ClearProfileResult.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile.results; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.ToString; 7 | 8 | /** 9 | * The ClearProfileResult object is send by the CPO to the given response_url in a POST request. 10 | * It contains the result of the DELETE (ClearProfile) request send by the eMSP 11 | */ 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode(callSuper = true) 15 | @NoArgsConstructor 16 | public class ClearProfileResult extends AbstractProfileResult { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/results/ChargingProfileResult.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile.results; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.ToString; 7 | 8 | /** 9 | * The ChargingProfileResult object is send by the CPO to the given response_url in a POST request. 10 | * It contains the result of the PUT (SetChargingProfile) request send by the eMSP. 11 | */ 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode(callSuper = true) 15 | @NoArgsConstructor 16 | public class ChargingProfileResult extends AbstractProfileResult { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/exception/PropertyConstraintException.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.exception; 2 | 3 | public class PropertyConstraintException extends IllegalArgumentException { 4 | private static final String EXCEPTION_MESSAGE_TEMPLATE = 5 | "Validation failed: [%s]. Current Value: [%s]"; 6 | 7 | public PropertyConstraintException(Object currentFieldValue, String errorMessage) { 8 | super(createValidationMessage(currentFieldValue, errorMessage)); 9 | } 10 | 11 | private static String createValidationMessage(Object fieldValue, String errorMessage) { 12 | return String.format(EXCEPTION_MESSAGE_TEMPLATE, errorMessage, fieldValue); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoChargingProfilesService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.ResponseFormat; 4 | import com.extrawest.ocpi.model.dto.charging_profile.ChargingProfileResponse; 5 | import com.extrawest.ocpi.model.dto.charging_profile.SetChargingProfile; 6 | 7 | public interface CpoChargingProfilesService { 8 | 9 | ChargingProfileResponse getChargingProfile(String sessionId, Integer duration, String responseUrl); 10 | 11 | ResponseFormat putChargingProfile(String sessionId, SetChargingProfile setChargingProfileRequestDTO); 12 | 13 | ChargingProfileResponse deleteChargingProfile(String sessionId, String responseUrl); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/CpoLocationService.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service; 2 | 3 | import com.extrawest.ocpi.model.dto.location.Connector; 4 | import com.extrawest.ocpi.model.dto.location.EVSE; 5 | import com.extrawest.ocpi.model.dto.location.Location; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | 10 | public interface CpoLocationService { 11 | 12 | List getLocations(LocalDateTime dateFrom, LocalDateTime dateTo, Integer offset, Integer limit); 13 | 14 | long getTotalCount(LocalDateTime dateFrom, LocalDateTime dateTo); 15 | 16 | Location getLocation(String locationId); 17 | 18 | EVSE getEvse(String locationId, String evseUid); 19 | 20 | Connector getConnector(String locationId, String evseUid, String connectorId); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/config/ObjectMapperConfig.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class ObjectMapperConfig { 12 | @Bean 13 | public ObjectMapper config() { 14 | ObjectMapper objectMapper = new ObjectMapper(); 15 | objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); 16 | objectMapper.registerModule(new JavaTimeModule()); 17 | return objectMapper; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/StopSession.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.Size; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * StopSession object, for the STOP_SESSION command, with information needed to stop sessions. 12 | */ 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @NoArgsConstructor 16 | public class StopSession extends AbstractCommand { 17 | 18 | /** 19 | * Session.id of the Session that is requested to be stopped 20 | */ 21 | @NotBlank 22 | @Size(min = 1, max = 36) 23 | @JsonProperty("session_id") 24 | private String sessionId; 25 | } -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/AbstractCommand.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.Size; 7 | 8 | public abstract class AbstractCommand { 9 | @JsonIgnore 10 | public String type; 11 | /** 12 | * URL that the CommandResult POST should be sent to. This URL might 13 | * contain unique ID to be able to distinguish between ReserveNow 14 | * requests. 15 | */ 16 | @NotBlank 17 | @Size(min = 1, max = 255) 18 | @JsonProperty("response_url") 19 | protected String responseUrl; 20 | 21 | public String getType() { 22 | return this.getClass().getSimpleName(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/VersionDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.enums.VersionNumber; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotNull; 8 | import jakarta.validation.constraints.Size; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class VersionDto implements OcpiResponseData, OcpiRequestData { 15 | /** 16 | * The version number 17 | */ 18 | @NotNull 19 | public VersionNumber version; 20 | 21 | /** 22 | * URL to the endpoint containing version specific information 23 | */ 24 | @NotBlank 25 | @Size(max = 255) 26 | public String url; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/tariff/TariffElement.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.tariff; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.validation.constraints.NotEmpty; 10 | import java.util.List; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class TariffElement implements OcpiRequestData, OcpiResponseData { 15 | /** 16 | * List of price components that describe the pricing of a tariff. 17 | */ 18 | @NotEmpty 19 | @JsonProperty("price_components") 20 | private List priceComponents; 21 | /** 22 | * Restrictions that describe the applicability of a tariff. 23 | */ 24 | private TariffRestrictions restrictions; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/DisplayText.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import jakarta.validation.constraints.Size; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import javax.validation.constraints.NotBlank; 10 | import javax.validation.constraints.NotEmpty; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class DisplayText implements OcpiRequestData, OcpiResponseData { 15 | 16 | /** 17 | * Language Code ISO 639-1. 18 | */ 19 | @NotEmpty 20 | @NotBlank 21 | @Size(min = 2, max = 2) 22 | private String language; 23 | /** 24 | * Text to be displayed to end user. No markup, html etc. allowed. 25 | */ 26 | @NotEmpty 27 | @NotBlank 28 | @Size(min = 1, max = 512) 29 | private String text; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/VersionDetailsDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.enums.VersionNumber; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import javax.validation.constraints.NotEmpty; 12 | import java.util.List; 13 | 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class VersionDetailsDto implements OcpiRequestData, OcpiResponseData { 18 | /** 19 | * The version number. 20 | */ 21 | @NotNull 22 | private VersionNumber version; 23 | 24 | /** 25 | * A list of supported endpoints for this version. 26 | */ 27 | @NotEmpty 28 | private List endpoints; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/ExceptionalPeriod.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotNull; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | /** 11 | * Specifies one exceptional period for opening or access hours. 12 | */ 13 | 14 | @Data 15 | @NoArgsConstructor 16 | public class ExceptionalPeriod { 17 | /** 18 | * Begin of the exception. In UTC, time_zone field can be used to convert to local time. 19 | */ 20 | @NotNull 21 | @JsonProperty("period_begin") 22 | private LocalDateTime periodBegin; 23 | /** 24 | * End of the exception. In UTC, time_zone field can be used to convert to local time. 25 | */ 26 | @NotNull 27 | @JsonProperty("period_end") 28 | private LocalDateTime periodEnd; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/Price.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.Digits; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | public class Price implements OcpiRequestData, OcpiResponseData { 14 | /** 15 | * Price/Cost excluding VAT. 16 | */ 17 | @NotNull 18 | @JsonProperty("excl_vat") 19 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 20 | private Float exclVat; 21 | /** 22 | * Price/Cost including VAT. 23 | */ 24 | @NotNull 25 | @JsonProperty("incl_vat") 26 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 27 | private Float inclVat; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/util/EnumUtil.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.util; 2 | 3 | import java.util.function.Function; 4 | 5 | public final class EnumUtil { 6 | 7 | private EnumUtil() { 8 | throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); 9 | } 10 | 11 | public static > T findByField(Class enumType, 12 | Function fieldSelector, 13 | String fieldValue) { 14 | for (T enumValue : enumType.getEnumConstants()) { 15 | if (fieldSelector.apply(enumValue).equals(fieldValue)) { 16 | return enumValue; 17 | } 18 | } 19 | throw new IllegalArgumentException(String.format( 20 | "No enum constant %s with field value %s", enumType.getName(), fieldValue 21 | )); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/results/ActiveChargingProfileResult.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile.results; 2 | 3 | import com.extrawest.ocpi.model.dto.charging_profile.ActiveChargingProfile; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * The ActiveChargingProfileResult object is send by the CPO to the given response_url in a POST request. 11 | * It contains the result of the GET (ActiveChargingProfile) request send by the eMSP. 12 | */ 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @NoArgsConstructor 16 | public class ActiveChargingProfileResult extends AbstractProfileResult { 17 | 18 | /** 19 | * The requested ActiveChargingProfile, if the result field is set to: ACCEPTED 20 | */ 21 | @JsonProperty("profile") 22 | private ActiveChargingProfile profile; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/CdrDimension.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.enums.CdrDimensionType; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.Digits; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class CdrDimension implements OcpiRequestData { 16 | 17 | /** 18 | * Type of CDR dimension. 19 | */ 20 | @JsonProperty("type") 21 | @NotNull 22 | private CdrDimensionType type; 23 | 24 | /** 25 | * Volume of the dimension consumed, measured according to the dimension type. 26 | */ 27 | @NotNull 28 | @JsonProperty("volume") 29 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 30 | private Float volume; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/EnvironmentalImpact.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.enums.EnvironmentalImpactCategory; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import jakarta.validation.constraints.Digits; 7 | import jakarta.validation.constraints.NotNull; 8 | import jakarta.validation.constraints.PositiveOrZero; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class EnvironmentalImpact implements OcpiRequestData, OcpiResponseData { 15 | 16 | /** 17 | * The environmental impact category of this value. 18 | */ 19 | @NotNull 20 | private EnvironmentalImpactCategory category; 21 | /** 22 | * Amount of this portion in g/kWh. 23 | */ 24 | @NotNull 25 | @PositiveOrZero 26 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 27 | private Float amount; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/BusinessDetails.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.dto.location.Image; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.Size; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class BusinessDetails implements OcpiRequestData, OcpiResponseData { 15 | 16 | /** 17 | * Name of the operator. 18 | */ 19 | @NotBlank 20 | @Size(max = 100) 21 | @JsonProperty("name") 22 | private String name; 23 | /** 24 | * Link to the operator’s website. 25 | */ 26 | @JsonProperty("website") 27 | private String website; 28 | /** 29 | * Image link to the operator’s logo. 30 | */ 31 | @JsonProperty("logo") 32 | private Image logo; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/ActiveChargingProfile.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotNull; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | /** 11 | * The ActiveChargingProfile is the charging profile as calculated by the EVSE. 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | public class ActiveChargingProfile { 16 | 17 | /** 18 | * Date and time at which the Charge Point has calculated this ActiveChargingProfile. 19 | * All time measurements within the profile are relative to this timestamp. 20 | */ 21 | @NotNull 22 | @JsonProperty("start_date_time") 23 | private LocalDateTime startDateTime; 24 | 25 | /** 26 | * Charging profile structure defines a list of charging periods. 27 | */ 28 | @NotNull 29 | @JsonProperty("charging_profile") 30 | private ChargingProfile chargingProfile; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/DayOfWeek.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | public enum DayOfWeek { 9 | MONDAY("MONDAY"), 10 | TUESDAY("TUESDAY"), 11 | WEDNESDAY("WEDNESDAY"), 12 | THURSDAY("THURSDAY"), 13 | FRIDAY("FRIDAY"), 14 | SATURDAY("SATURDAY"), 15 | SUNDAY("SUNDAY"); 16 | private final String value; 17 | 18 | DayOfWeek(String value) { 19 | this.value = value; 20 | } 21 | 22 | @JsonCreator 23 | public static DayOfWeek fromValue(String value) { 24 | return findByField( 25 | DayOfWeek.class, 26 | DayOfWeek::value, 27 | value 28 | ); 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return this.value; 34 | } 35 | 36 | @JsonValue 37 | public String value() { 38 | return this.value; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/CommandResult.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.extrawest.ocpi.model.dto.DisplayText; 4 | import com.extrawest.ocpi.model.enums.CommandResultType; 5 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.NotBlank; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | /** 13 | * Result of the command request, from the Charge Point 14 | */ 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class CommandResult implements OcpiRequestData { 19 | 20 | /** 21 | * Result of the command request as sent by the Charge Point to the CPO. 22 | */ 23 | @NotBlank 24 | @JsonProperty("result") 25 | private CommandResultType result; 26 | 27 | /** 28 | * Result of the command request as sent by the Charge Point to the CPO. 29 | */ 30 | @JsonProperty("message") 31 | private DisplayText message; 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/release-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish package to the Maven Central Repository 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | pull_request: 8 | branches: [ main ] 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set up JDK 17 16 | uses: actions/setup-java@v3 17 | with: 18 | java-version: '17' 19 | distribution: 'adopt' 20 | - name: Publish package 21 | env: 22 | JRELEASER_NEXUS2_USERNAME: ${{ secrets.JRELEASER_NEXUS2_USERNAME }} 23 | JRELEASER_NEXUS2_PASSWORD: ${{ secrets.JRELEASER_NEXUS2_PASSWORD }} 24 | JRELEASER_GPG_PASSPHRASE: ${{ secrets.JRELEASER_GPG_PASSPHRASE }} 25 | JRELEASER_GPG_SECRET_KEY: ${{ secrets.JRELEASER_GPG_SECRET_KEY }} 26 | JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.JRELEASER_GPG_PUBLIC_KEY }} 27 | JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | run: mvn -Prelease deploy jreleaser:deploy -DaltDeploymentRepository=local::file:./target/staging-deploy -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/ChargingProfilePeriod.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotNull; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Charging profile period structure defines a time period in a charging profile, as used in: ChargingProfile 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | public class ChargingProfilePeriod { 14 | /** 15 | * Start of the period, in seconds from the start of profile. 16 | * The value of StartPeriod also defines the stop time of the previous period. 17 | */ 18 | @NotNull 19 | @JsonProperty("start_period") 20 | private Integer startPeriod; 21 | 22 | /** 23 | * Charging rate limit during the profile period, in the applicable chargingRateUnit, for example in Amperes (A) 24 | * or Watts (W). Accepts at most one digit fraction (e.g. 8.1). 25 | */ 26 | @NotNull 27 | @JsonProperty("limit") 28 | private Float limit; 29 | } 30 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/Endpoint.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.enums.InterfaceRole; 4 | import com.extrawest.ocpi.model.enums.ModuleID; 5 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 6 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | import jakarta.validation.constraints.NotBlank; 9 | import jakarta.validation.constraints.NotNull; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | 13 | @Data 14 | @NoArgsConstructor 15 | public class Endpoint implements OcpiRequestData, OcpiResponseData { 16 | /** 17 | * Endpoint identifier. 18 | */ 19 | @NotNull 20 | @JsonProperty("identifier") 21 | private ModuleID identifier; 22 | /** 23 | * Interface role this endpoint implements. 24 | */ 25 | @NotNull 26 | @JsonProperty("role") 27 | private InterfaceRole role; 28 | /** 29 | * URL to the endpoint. 30 | */ 31 | @NotBlank 32 | @JsonProperty("url") 33 | private String url; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/PaginationHeaders.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | 4 | public class PaginationHeaders { 5 | /* Link to the 'next' page should be provided when this is NOT the last page. The Link 6 | should also contain any filters present in the original request. 7 | */ 8 | public final static String LINK = "Link"; 9 | 10 | /* 11 | The total number of objects available in the server system that match the given query (including the given query 12 | parameters, for example: date_to and date_from but excluding limit and offset) and that are available to this 13 | client. 14 | */ 15 | public final static String X_TOTAL_COUNT = "X-Total-Count"; 16 | 17 | /* The maximum number of objects that the server can return. 18 | Note that this is an upper limit. If there are not enough remaining objects to return, 19 | fewer objects than this upper limit number will be returned, X-Limit SHALL then still 20 | show the upper limit, not the number of objects returned. 21 | */ 22 | public final static String X_LIMIT = "X-Limit"; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/Status.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum Status { 8 | AVAILABLE("AVAILABLE"), 9 | BLOCKED("BLOCKED"), 10 | CHARGING("CHARGING"), 11 | INOPERATIVE("INOPERATIVE"), 12 | OUTOFORDER("OUTOFORDER"), 13 | PLANNED("PLANNED"), 14 | REMOVED("REMOVED"), 15 | RESERVED("RESERVED"), 16 | UNKNOWN("UNKNOWN"); 17 | private final String value; 18 | 19 | Status(String value) { 20 | this.value = value; 21 | } 22 | 23 | @JsonCreator 24 | public static Status fromValue(String value) { 25 | return EnumUtil.findByField( 26 | Status.class, 27 | Status::value, 28 | value 29 | ); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return this.value; 35 | } 36 | 37 | @JsonValue 38 | public String value() { 39 | return this.value; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Extrawest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/CredentialsDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotEmpty; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class CredentialsDto implements OcpiRequestData, OcpiResponseData { 18 | /** 19 | * The credentials token for the other party to authenticate in your system. 20 | */ 21 | @Size(min = 1, max = 255) 22 | @NotBlank 23 | private String token; 24 | 25 | /** 26 | * The URL to your API versions endpoint. 27 | */ 28 | @NotBlank 29 | @Size(max = 255) 30 | private String url; 31 | 32 | /** 33 | * List of the roles this party provides. 34 | */ 35 | @NotEmpty 36 | private List roles; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/SetChargingProfile.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotNull; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * SetChargingProfile object with information needed to set/update the Charging Profile for a session. 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class SetChargingProfile { 17 | /** 18 | * Contains limits for the available power or current over time. 19 | */ 20 | @NotNull 21 | @JsonProperty("charging_profile") 22 | private ChargingProfile chargingProfile; 23 | 24 | /** 25 | * URL that the ChargingProfileResult POST should be sent to. This URL might contain unique ID to be able to 26 | * distinguish between GET ActiveChargingProfile requests. 27 | */ 28 | @NotBlank 29 | @JsonProperty("response_url ") 30 | private String responseUrl; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/EnergySource.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.enums.EnergySourceCategory; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.Digits; 8 | import jakarta.validation.constraints.Max; 9 | import jakarta.validation.constraints.Min; 10 | import jakarta.validation.constraints.NotNull; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | public class EnergySource implements OcpiRequestData, OcpiResponseData { 17 | /** 18 | * The type of energy source. 19 | */ 20 | @JsonProperty("source") 21 | @NotNull 22 | private EnergySourceCategory source; 23 | 24 | /** 25 | * Percentage of this source (0-100) in the mix. 26 | */ 27 | @JsonProperty("percentage") 28 | @NotNull 29 | @Min(0) 30 | @Max(100) 31 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 32 | private Float percentage; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ConnectorFormat.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum ConnectorFormat { 8 | /** 9 | * The connector is a socket; the EV user needs to bring a fitting plug. 10 | */ 11 | SOCKET("SOCKET"), 12 | /** 13 | * The connector is an attached cable; the EV users car needs to have a fitting inlet. 14 | */ 15 | CABLE("CABLE"); 16 | private final String value; 17 | 18 | ConnectorFormat(String value) { 19 | this.value = value; 20 | } 21 | 22 | @JsonCreator 23 | public static ConnectorFormat fromValue(String value) { 24 | return EnumUtil.findByField( 25 | ConnectorFormat.class, 26 | ConnectorFormat::value, 27 | value 28 | ); 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return this.value; 34 | } 35 | 36 | @JsonValue 37 | public String value() { 38 | return this.value; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/ChargingProfileResponse.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile; 2 | 3 | import com.extrawest.ocpi.model.enums.ChargingProfileResponseType; 4 | import jakarta.validation.constraints.NotNull; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * Result of the ActiveChargingProfile request, by the Receiver (Typically CPO), not the 11 | * location/EVSE. So this indicates if the Receiver understood the ChargingProfile request 12 | * and was able to send it to the EVSE. This is not the response by the Charge Point. 13 | */ 14 | 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class ChargingProfileResponse { 19 | /** 20 | * Response from the CPO on the ChargingProfile request. 21 | */ 22 | @NotNull 23 | private ChargingProfileResponseType result; 24 | 25 | /** 26 | * Timeout for this ChargingProfile request in seconds. When the Result is not received within this timeout, 27 | * the eMSP can assume that the message might never be sent. 28 | */ 29 | @NotNull 30 | private Integer timeout; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/CommandResponse.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.extrawest.ocpi.model.dto.DisplayText; 4 | import com.extrawest.ocpi.model.enums.CommandResponseType; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.List; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class CommandResponse { 15 | 16 | /** 17 | * Response from the CPO on the command request. 18 | */ 19 | @NotNull 20 | @JsonProperty("result") 21 | private CommandResponseType result; 22 | 23 | /** 24 | * Timeout for this command in seconds. When the Result is not received within this timeout, 25 | * the eMSP can assume that the message might never be sent. 26 | */ 27 | @NotNull 28 | @JsonProperty("timeout") 29 | private Integer timeout; 30 | 31 | /** 32 | * Human-readable description of the result (if one can be provided), multiple languages can be provided. 33 | */ 34 | @JsonProperty("message") 35 | private List message; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/token/EnergyContract.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.token; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotNull; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | /** 13 | * Information about a energy contract that belongs to a Token so a driver could use his/her own energy contract 14 | * when charging at a Charge Point. 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class EnergyContract implements OcpiRequestData, OcpiResponseData { 20 | /** 21 | * Name of the energy supplier for this token. 22 | */ 23 | @NotNull 24 | @Size(max = 61) 25 | @JsonProperty("supplier_name") 26 | private String supplierName; 27 | 28 | /** 29 | * Contract ID at the energy supplier, that belongs to the owner of this token. 30 | */ 31 | @JsonProperty("contract_id") 32 | private String contractId; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/CancelReservation.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.Size; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * With CancelReservation the Sender can request the Cancel of an existing Reservation. 12 | * The CancelReservation needs to contain the reservation_id that was given by the Sender to the ReserveNow. 13 | * As there might be cost involved for a Reservation, canceling a reservation might still result 14 | * in a CDR being sent for the reservation. 15 | */ 16 | @Data 17 | @EqualsAndHashCode(callSuper = true) 18 | @NoArgsConstructor 19 | public class CancelReservation extends AbstractCommand { 20 | 21 | /** 22 | * Reservation id, unique for this reservation. If the Charge Point already has a reservation that matches 23 | * this reservationId the Charge Point will replace the reservation. 24 | */ 25 | @NotBlank 26 | @Size(min = 1, max = 36) 27 | @JsonProperty("reservation_id") 28 | private String reservationId; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/RegularHours.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.*; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Regular recurring operation or access hours. 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | public class RegularHours { 14 | /** 15 | * Number of day in the week, from Monday (1) till Sunday (7) 16 | */ 17 | @NotNull 18 | @Min(1) 19 | @Max(7) 20 | @JsonProperty("weekday") 21 | private Integer weekday; 22 | /** 23 | * Begin of the regular period, in local time, given in hours and minutes. Must be in 24h format with leading zeros. 24 | * Example: "18:15". Hour/Minute separator: ":" Regex: ([0-1][0-9]|2[0-3]):[0-5][0-9]. 25 | */ 26 | @NotBlank 27 | @Size(max = 5) 28 | @JsonProperty("period_begin") 29 | private String periodBegin; 30 | /** 31 | * End of the regular period, in local time, syntax as for period_begin. Must be later than period_begin. 32 | */ 33 | @NotBlank 34 | @Size(max = 5) 35 | @JsonProperty("period_end") 36 | private String periodEnd; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/EnvironmentalImpactCategory.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | public enum EnvironmentalImpactCategory { 9 | /** 10 | * Produced nuclear waste in grams per kilowatt/hour. 11 | */ 12 | NUCLEAR_WASTE("NUCLEAR_WASTE"), 13 | /** 14 | * Exhausted carbon dioxide in grams per kilowatt/hour. 15 | */ 16 | CARBON_DIOXIDE("CARBON_DIOXIDE"); 17 | private final String value; 18 | 19 | EnvironmentalImpactCategory(String value) { 20 | this.value = value; 21 | } 22 | 23 | @JsonCreator 24 | public static EnvironmentalImpactCategory fromValue(String value) { 25 | return findByField( 26 | EnvironmentalImpactCategory.class, 27 | EnvironmentalImpactCategory::value, 28 | value 29 | ); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return this.value; 35 | } 36 | 37 | @JsonValue 38 | public String value() { 39 | return this.value; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/GeoLocation.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * This class defines the geolocation of the Charge Point. The geodetic system to be used is WGS 84. 13 | */ 14 | @Data 15 | @NoArgsConstructor 16 | public class GeoLocation implements OcpiRequestData, OcpiResponseData { 17 | 18 | /** 19 | * Latitude of the point in decimal degree. Example: 50.770774. Decimal separator: "." 20 | * Regex: -?[0-9]{1,2}\.[0-9]{5,7} 21 | */ 22 | @NotBlank 23 | @Size(max = 10) 24 | @JsonProperty("latitude") 25 | private String latitude; 26 | /** 27 | * Longitude of the point in decimal degree. Example: -126.104965. Decimal separator: "." 28 | * Regex: -?[0-9]{1,3}\.[0-9]{5,7} 29 | */ 30 | @NotBlank 31 | @Size(max = 11) 32 | @JsonProperty("longitude") 33 | private String longitude; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/InterfaceRole.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | public enum InterfaceRole { 9 | /** 10 | * Sender Interface implementation. Interface implemented by the owner of data, so the Receiver can 11 | * Pull information from the data Sender/owner. 12 | */ 13 | SENDER("SENDER"), 14 | /** 15 | * Receiver Interface implementation. Interface implemented by the receiver of data, so the 16 | * Sender/owner can Push information to the Receiver. 17 | */ 18 | RECEIVER("RECEIVER"); 19 | private final String value; 20 | 21 | InterfaceRole(String value) { 22 | this.value = value; 23 | } 24 | 25 | @JsonCreator 26 | public static InterfaceRole fromValue(String value) { 27 | return findByField( 28 | InterfaceRole.class, 29 | InterfaceRole::value, 30 | value 31 | ); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return this.value; 37 | } 38 | 39 | @JsonValue 40 | public String value() { 41 | return this.value; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ConnectionStatus.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum ConnectionStatus { 8 | /** 9 | * Party is connected. 10 | */ 11 | CONNECTED("CONNECTED"), 12 | /** 13 | * Party is currently not connected. 14 | */ 15 | OFFLINE("OFFLINE"), 16 | /** 17 | * Connection to this party is planned, but has never been connected. 18 | */ 19 | PLANNED("PLANNED"), 20 | /** 21 | * Party is now longer active, will never connect anymore. 22 | */ 23 | SUSPENDED("SUSPENDED"); 24 | private final String value; 25 | 26 | ConnectionStatus(String value) { 27 | this.value = value; 28 | } 29 | 30 | @JsonCreator 31 | public static ConnectionStatus fromValue(String value) { 32 | return EnumUtil.findByField( 33 | ConnectionStatus.class, 34 | ConnectionStatus::value, 35 | value 36 | ); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return this.value; 42 | } 43 | 44 | @JsonValue 45 | public String value() { 46 | return this.value; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/LocationReferencesDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.extrawest.ocpi.util.Constants; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.Pattern; 9 | import jakarta.validation.constraints.Size; 10 | import lombok.AllArgsConstructor; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * Location and EVSEs for which the Token is requested to be authorized. 18 | */ 19 | 20 | @Data 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | public class LocationReferencesDto implements OcpiResponseData, OcpiRequestData { 24 | 25 | /** 26 | * Unique identifier for the location 27 | */ 28 | @NotBlank 29 | @Size(min = 1, max = 36) 30 | @JsonProperty("location_id") 31 | private String locationId; 32 | 33 | /** 34 | * Unique identifiers for EVSEs within the CPO’s platform for the EVSE within the 35 | * given location. 36 | */ 37 | @JsonProperty("evse_uids") 38 | private List<@Size(min = 1, max = 36) @Pattern(regexp = Constants.ASCII_REGEXP) String> evseUids; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ReservationRestrictionType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | public enum ReservationRestrictionType { 9 | /** 10 | * Used in TariffElements to describe costs for a reservation. 11 | */ 12 | RESERVATION("RESERVATION"), 13 | /** 14 | * Used in TariffElements to describe costs for a reservation that expires (i.e. driver does not start a 15 | * charging session before expiry_date of the reservation) 16 | */ 17 | RESERVATION_EXPIRES("RESERVATION_EXPIRES"); 18 | private final String value; 19 | 20 | ReservationRestrictionType(String value) { 21 | this.value = value; 22 | } 23 | 24 | @JsonCreator 25 | public static ReservationRestrictionType fromValue(String value) { 26 | return findByField( 27 | ReservationRestrictionType.class, 28 | ReservationRestrictionType::value, 29 | value 30 | ); 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return this.value; 36 | } 37 | 38 | @JsonValue 39 | public String value() { 40 | return this.value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/security/Permission.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.security; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | @Getter 7 | @AllArgsConstructor 8 | public enum Permission { 9 | //https://github.com/ocpi/ocpi/blob/480973547169dee2fe6d12b1a0fe604623efcbb5/terminology.asciidoc#typical-ocpi-implementations-per-role 10 | CDRS_SENDER("cdrs:send"), 11 | CDRS_RECEIVER("cdrs:receive"), 12 | CHARGING_PROFILES_SENDER("chargingprofiles:send"), 13 | CHARGING_PROFILES_RECEIVER("chargingprofiles:receive"), 14 | COMMANDS_SENDER("commands:send"), 15 | COMMANDS_RECEIVER("commands:receive"), 16 | CREDENTIALS_SENDER("credentials:send"), 17 | CREDENTIALS_RECEIVER("credentials:receive"), 18 | HUB_CLIENT_INFO_SENDER("hubclientinfo:send"), 19 | HUB_CLIENT_INFO_RECEIVER("hubclientinfo:receive"), 20 | LOCATIONS_SENDER("locations:send"), 21 | LOCATIONS_RECEIVER("locations:receive"), 22 | SESSIONS_SENDER("sessions:send"), 23 | SESSIONS_RECEIVER("sessions:receive"), 24 | TARIFFS_SENDER("tariffs:send"), 25 | TARIFFS_RECEIVER("tariffs:receive"), 26 | TOKENS_SENDER("tokens:send"), 27 | TOKENS_RECEIVER("tokens:receive"), 28 | VERSIONS_RECEIVER("versions:send"), 29 | VERSIONS_SENDER("versions:receive"); 30 | 31 | private final String permission; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/PowerType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum PowerType { 8 | /** 9 | * AC single phase 10 | */ 11 | AC_1_PHASE("AC_1_PHASE"), 12 | /** 13 | * AC two phases, only two of the three available phases connected. 14 | */ 15 | AC_2_PHASE("AC_2_PHASE"), 16 | /** 17 | * AC two phases using split phase system. 18 | */ 19 | AC_2_PHASE_SPLIT("AC_2_PHASE_SPLIT"), 20 | /** 21 | * AC two phases using split phase system. 22 | */ 23 | AC_3_PHASE("AC_3_PHASE"), 24 | /** 25 | * Direct Current. 26 | */ 27 | DC("DC"); 28 | private final String value; 29 | 30 | PowerType(String value) { 31 | this.value = value; 32 | } 33 | 34 | @JsonCreator 35 | public static PowerType fromValue(String value) { 36 | return EnumUtil.findByField( 37 | PowerType.class, 38 | PowerType::value, 39 | value 40 | ); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return this.value; 46 | } 47 | 48 | @JsonValue 49 | public String value() { 50 | return this.value; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/CredentialsRole.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.enums.Role; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.NotNull; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | 15 | @Data 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @Builder 19 | public class CredentialsRole implements OcpiRequestData, OcpiResponseData { 20 | /** 21 | * Type of role. 22 | */ 23 | @NotNull 24 | @JsonProperty("role") 25 | private Role role; 26 | /** 27 | * Details of this party. 28 | */ 29 | @NotNull 30 | @JsonProperty("business_details") 31 | private BusinessDetails businessDetails; 32 | /** 33 | * CPO, eMSP (or other role) ID of this party (following the ISO-15118 standard). 34 | */ 35 | @NotBlank 36 | @JsonProperty("party_id") 37 | private String partyId; 38 | /** 39 | * ISO-3166 alpha-2 country code of the country this party is operating in. 40 | */ 41 | @NotBlank 42 | @JsonProperty("country_code") 43 | private String countryCode; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ModuleID.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Most modules (except Credentials, Registration) are optional, but there might be dependencies between modules. 9 | */ 10 | public enum ModuleID { 11 | CDRS("cdrs"), 12 | CHARGING_PROFILES("chargingprofiles"), 13 | COMMANDS("commands"), 14 | /** 15 | * Required for all implementations. 16 | * The role field has no function for this module. 17 | */ 18 | CREDENTIALS("credentials"), 19 | HUB_CLIENT_INFO("hubclientinfo"), 20 | LOCATIONS("locations"), 21 | SESSIONS("sessions"), 22 | TARIFFS("tariffs"), 23 | TOKENS("tokens"); 24 | private final String value; 25 | 26 | ModuleID(String value) { 27 | this.value = value; 28 | } 29 | 30 | @JsonCreator 31 | public static ModuleID fromValue(String value) { 32 | return EnumUtil.findByField( 33 | ModuleID.class, 34 | ModuleID::value, 35 | value 36 | ); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return this.value; 42 | } 43 | 44 | @JsonValue 45 | public String value() { 46 | return this.value; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ChargingProfileResultType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Result of a ChargingProfile request that the EVSE sends via the CPO to the eMSP. 9 | */ 10 | public enum ChargingProfileResultType { 11 | /** 12 | * ChargingProfile request accepted by the EVSE. 13 | */ 14 | ACCEPTED("ACCEPTED"), 15 | /** 16 | * ChargingProfile request rejected by the EVSE. 17 | */ 18 | REJECTED("REJECTED"), 19 | /** 20 | * No Charging Profile(s) were found by the EVSE matching the request. 21 | */ 22 | UNKNOWN("UNKNOWN"); 23 | private final String value; 24 | 25 | ChargingProfileResultType(String value) { 26 | this.value = value; 27 | } 28 | 29 | @JsonCreator 30 | public static ChargingProfileResultType fromValue(String value) { 31 | return EnumUtil.findByField( 32 | ChargingProfileResultType.class, 33 | ChargingProfileResultType::value, 34 | value 35 | ); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return this.value; 41 | } 42 | 43 | @JsonValue 44 | public String value() { 45 | return this.value; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/TokenType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum TokenType { 8 | /** 9 | * One time use Token ID generated by a server (or App.) The eMSP uses this to bind a Session to a 10 | * customer, probably an app user. 11 | */ 12 | AD_HOC_USER("AD_HOC_USER"), 13 | /** 14 | * Token ID generated by a server (or App.) to identify a user of an App. The same user uses the 15 | * same Token for every Session. 16 | */ 17 | APP_USER("APP_USER"), 18 | /** 19 | * Other type of token 20 | */ 21 | OTHER("OTHER"), 22 | /** 23 | * RFID Token 24 | */ 25 | RFID("RFID"); 26 | private final String value; 27 | 28 | TokenType(String value) { 29 | this.value = value; 30 | } 31 | 32 | @JsonCreator 33 | public static TokenType fromValue(String value) { 34 | return EnumUtil.findByField( 35 | TokenType.class, 36 | TokenType::value, 37 | value 38 | ); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return this.value; 44 | } 45 | 46 | @JsonValue 47 | public String value() { 48 | return this.value; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/EnergyMix.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.List; 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class EnergyMix implements OcpiRequestData, OcpiResponseData { 15 | 16 | /** 17 | * True if 100% from regenerative sources. (CO2 and nuclear waste is zero) 18 | */ 19 | @NotNull 20 | @JsonProperty("is_green_energy") 21 | private boolean is_green_energy; 22 | /** 23 | * Key-value pairs (enum + percentage) of energy sources of this location’s tariff. 24 | */ 25 | private List energy_sources; 26 | /** 27 | * Key-value pairs (enum + percentage) of nuclear waste and CO2 exhaust of this location’s tariff. 28 | */ 29 | private List environ_impact; 30 | /** 31 | * Name of the energy supplier, delivering the energy for this location or tariff. 32 | */ 33 | private String supplier_name; 34 | /** 35 | * Name of the energy suppliers product/tariff plan used at this location. 36 | */ 37 | private String energy_product_name; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/VersionNumber.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * List of known versions. 9 | */ 10 | public enum VersionNumber { 11 | /** 12 | * OCPI version 2.0 13 | */ 14 | V_2_0("2.0"), 15 | /** 16 | * OCPI version 2.1 (DEPRECATED, do not use, use 2.1.1 instead) 17 | */ 18 | V_2_1("2.1"), 19 | /** 20 | * OCPI version 2.1.1 21 | */ 22 | V_2_1_1("2.1.1"), 23 | /** 24 | * OCPI version 2.2 (DEPRECATED, do not use, use 2.2.1 instead) 25 | */ 26 | V_2_2("2.2"), 27 | /** 28 | * OCPI version 2.2.1 (this version) 29 | */ 30 | V_2_2_1("2.2.1"); 31 | private final String value; 32 | 33 | VersionNumber(String value) { 34 | this.value = value; 35 | } 36 | 37 | @JsonCreator 38 | public static VersionNumber fromValue(String value) { 39 | return EnumUtil.findByField( 40 | VersionNumber.class, 41 | VersionNumber::value, 42 | value 43 | ); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return this.value; 49 | } 50 | 51 | @JsonValue 52 | public String value() { 53 | return this.value; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/StatusSchedule.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.enums.Status; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * This type is used to schedule status periods in the future. The eMSP can provide this information to the EV user 16 | * for trip planning purposes. A period MAY have no end. Example: "This station will be running as of tomorrow. 17 | * Today it is still planned and under construction." 18 | */ 19 | @Data 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | public class StatusSchedule implements OcpiResponseData, OcpiRequestData { 23 | 24 | /** 25 | * Begin of the scheduled period. 26 | */ 27 | @NotNull 28 | @JsonProperty("period_begin") 29 | private LocalDateTime periodBegin; 30 | /** 31 | * End of the scheduled period, if known. 32 | */ 33 | @JsonProperty("period_end") 34 | private LocalDateTime periodEnd; 35 | /** 36 | * Status value during the scheduled period. 37 | */ 38 | @NotNull 39 | private Status status; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/UnlockConnector.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.Size; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * UnlockConnector object, for the UNLOCK_CONNECTOR command, with information needed to unlock a connector 12 | * of a Charge Point. 13 | */ 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @NoArgsConstructor 17 | public class UnlockConnector extends AbstractCommand { 18 | 19 | /** 20 | * Location.id of the Location (belonging to the CPO this request is send to) of which it is requested 21 | * to unlock the connector. 22 | */ 23 | @NotBlank 24 | @Size(min = 1, max = 36) 25 | @JsonProperty("location_id") 26 | private String locationId; 27 | 28 | /** 29 | * EVSE.uid of the EVSE of this Location of which it is requested to unlock the connector. 30 | */ 31 | @NotBlank 32 | @Size(min = 1, max = 36) 33 | @JsonProperty("evse_uid") 34 | private String evseUid; 35 | 36 | /** 37 | * Connector.id of the Connector of this Location of which it is requested to unlock. 38 | */ 39 | @NotBlank 40 | @Size(min = 1, max = 36) 41 | @JsonProperty("connector_id") 42 | private String connectorId; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/AdditionalGeoLocation.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.dto.DisplayText; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.Getter; 10 | import lombok.NoArgsConstructor; 11 | import lombok.ToString; 12 | 13 | /** 14 | * This class defines an additional geolocation that is relevant for the Charge Point. 15 | * The geodetic system to be used is WGS 84. 16 | */ 17 | @Getter 18 | @ToString 19 | @EqualsAndHashCode(callSuper = false) 20 | @NoArgsConstructor 21 | public class AdditionalGeoLocation implements OcpiRequestData { 22 | /** 23 | * Latitude of the point in decimal degree. Example: 50.770774. Decimal separator: "." 24 | * Regex: -?[0-9]{1,2}\.[0-9]{5,7} 25 | */ 26 | @NotBlank 27 | @Size(max = 10) 28 | @JsonProperty("latitude") 29 | private String latitude; 30 | /** 31 | * Longitude of the point in decimal degree. Example: -126.104965. Decimal separator: "." 32 | * Regex: -?[0-9]{1,3}\.[0-9]{5,7} 33 | */ 34 | @NotBlank 35 | @Size(max = 11) 36 | @JsonProperty("longitude") 37 | private String longitude; 38 | 39 | @JsonProperty("name") 40 | private DisplayText name; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/AllowedType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum AllowedType { 8 | /** 9 | * This Token is allowed to charge (at this location). 10 | */ 11 | ALLOWED("ALLOWED"), 12 | /** 13 | * This Token is blocked. 14 | */ 15 | BLOCKED("BLOCKED"), 16 | /** 17 | * This Token has expired. 18 | */ 19 | EXPIRED("EXPIRED"), 20 | /** 21 | * This Token belongs to an account that has not enough credits to charge (at the given location). 22 | */ 23 | NO_CREDIT("NO_CREDIT"), 24 | /** 25 | * Token is valid, but is not allowed to charge at the given location. 26 | */ 27 | NOT_ALLOWED("NOT_ALLOWED"); 28 | private final String value; 29 | 30 | AllowedType(String value) { 31 | this.value = value; 32 | } 33 | 34 | @JsonCreator 35 | public static AllowedType fromValue(String value) { 36 | return EnumUtil.findByField( 37 | AllowedType.class, 38 | AllowedType::value, 39 | value 40 | ); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return this.value; 46 | } 47 | 48 | @JsonValue 49 | public String value() { 50 | return this.value; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ProfileType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Different smart charging profile types. 9 | */ 10 | public enum ProfileType { 11 | /** 12 | * Driver wants to use the cheapest charging profile possible. 13 | */ 14 | CHEAP("CHEAP"), 15 | /** 16 | * Driver wants his EV charged as quickly as possible and is willing to pay a premium for this, if needed. 17 | */ 18 | FAST("FAST"), 19 | /** 20 | * Driver wants his EV charged with as much regenerative (green) energy as possible. 21 | */ 22 | GREEN("GREEN"), 23 | /** 24 | * Driver does not have special preferences. 25 | */ 26 | REGULAR("REGULAR"); 27 | private final String value; 28 | 29 | ProfileType(String value) { 30 | this.value = value; 31 | } 32 | 33 | @JsonCreator 34 | public static ProfileType fromValue(String value) { 35 | return EnumUtil.findByField( 36 | ProfileType.class, 37 | ProfileType::value, 38 | value 39 | ); 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return this.value; 45 | } 46 | 47 | @JsonValue 48 | public String value() { 49 | return this.value; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/TariffDimensionType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | public enum TariffDimensionType { 9 | /** 10 | * Defined in kWh, step_size multiplier: 1 Wh 11 | */ 12 | ENERGY("ENERGY"), 13 | /** 14 | * Flat fee without unit for step_size 15 | */ 16 | FLAT("FLAT"), 17 | /** 18 | * Time not charging: defined in hours, step_size multiplier: 1 second 19 | */ 20 | PARKING_TIME("PARKING_TIME"), 21 | /** 22 | * Time charging: defined in hours, step_size multiplier: 1 second 23 | * Can also be used in combination with a RESERVATION restriction to describe the price of the reservation time. 24 | */ 25 | TIME("TIME"); 26 | private final String value; 27 | 28 | TariffDimensionType(String value) { 29 | this.value = value; 30 | } 31 | 32 | @JsonCreator 33 | public static TariffDimensionType fromValue(String value) { 34 | return findByField( 35 | TariffDimensionType.class, 36 | TariffDimensionType::value, 37 | value 38 | ); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return this.value; 44 | } 45 | 46 | @JsonValue 47 | public String value() { 48 | return this.value; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/Role.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | public enum Role { 9 | /** 10 | * Charge Point Operator Role. 11 | */ 12 | CPO("CPO"), 13 | /** 14 | * eMobility Service Provider Role. 15 | */ 16 | EMSP("EMSP"), 17 | /** 18 | * Hub role. 19 | */ 20 | HUB("HUB"), 21 | /** 22 | * National Access Point Role (national Database with all Location information of a country). 23 | */ 24 | NAP("NAP"), 25 | /** 26 | * Navigation Service Provider Role, role like an eMSP (probably only interested in Location information). 27 | */ 28 | NSP("NSP"), 29 | /** 30 | * Other role. 31 | */ 32 | OTHER("OTHER"), 33 | /** 34 | * Smart Charging Service Provider Role. 35 | */ 36 | SCSP("SCSP"); 37 | private final String value; 38 | 39 | Role(String value) { 40 | this.value = value; 41 | } 42 | 43 | @JsonCreator 44 | public static Role fromValue(String value) { 45 | return findByField( 46 | Role.class, 47 | Role::value, 48 | value 49 | ); 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return this.value; 55 | } 56 | 57 | @JsonValue 58 | public String value() { 59 | return this.value; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/CommandResponseType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Response to the command request from the eMSP to the CPO. 9 | */ 10 | public enum CommandResponseType { 11 | /** 12 | * The requested command is not supported by this CPO, Charge Point, EVSE etc. 13 | */ 14 | NOT_SUPPORTED("NOT_SUPPORTED"), 15 | /** 16 | * Command request rejected by the CPO. (Session might not be from a customer of the eMSP that send this request) 17 | */ 18 | REJECTED("REJECTED"), 19 | /** 20 | * Command request accepted by the CPO. 21 | */ 22 | ACCEPTED("ACCEPTED"), 23 | /** 24 | * The Session in the requested command is not known by this CPO. 25 | */ 26 | UNKNOWN_SESSION("UNKNOWN_SESSION"); 27 | private final String value; 28 | 29 | CommandResponseType(String value) { 30 | this.value = value; 31 | } 32 | 33 | @JsonCreator 34 | public static CommandResponseType fromValue(String value) { 35 | return EnumUtil.findByField( 36 | CommandResponseType.class, 37 | CommandResponseType::value, 38 | value 39 | ); 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return this.value; 45 | } 46 | 47 | @JsonValue 48 | public String value() { 49 | return this.value; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/cdr/SignedValue.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.cdr; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.Size; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * This class contains the signed and the plain/unsigned data. By decoding the data, the receiver can check 11 | * if the content has not been altered. 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | public class SignedValue { 16 | 17 | /** 18 | * Nature of the value, in other words, the event this value belongs to. 19 | * Possible values at moment of writing: 20 | * - Start (value at the start of the Session) 21 | * - End (signed value at the end of the Session) 22 | * - Intermediate (signed values take during the Session, after Start, before End) 23 | * Others might be added later. 24 | */ 25 | @NotBlank 26 | @JsonProperty("nature") 27 | @Size(max = 32) 28 | private String nature; 29 | 30 | /** 31 | * The un-encoded string of data. The format of the content depends on the EncodingMethod field. 32 | */ 33 | @NotBlank 34 | @Size(max = 512) 35 | @JsonProperty("plain_data") 36 | private String plainData; 37 | 38 | /** 39 | * Blob of signed data, base64 encoded. The format of the content depends on the EncodingMethod field. 40 | */ 41 | @NotBlank 42 | @Size(max = 5000) 43 | @JsonProperty("signed_data") 44 | private String signedData; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/validation/ClientObjectValidation.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.validation; 2 | 3 | import com.extrawest.ocpi.exception.OcpiGeneralClientException; 4 | import com.extrawest.ocpi.exception.OcpiInvalidParametersException; 5 | import com.extrawest.ocpi.model.dto.ClientOwnedObject; 6 | import com.extrawest.ocpi.model.dto.token.TokenDto; 7 | import lombok.experimental.UtilityClass; 8 | 9 | import java.util.Objects; 10 | 11 | @UtilityClass 12 | public class ClientObjectValidation { 13 | /** 14 | * @throws OcpiGeneralClientException if country_code and/or party_id in request parameters is other than 15 | * country_code and/or party_id in request body 16 | * @throws OcpiInvalidParametersException if tariff_id in request parameters is other than in request body 17 | */ 18 | 19 | public static void checkClientCanModifyObject(TokenDto object, 20 | String countryCode, 21 | String partyId, 22 | String uid) { 23 | if (!Objects.equals(object.getCountryCode(), countryCode) 24 | || !Objects.equals(object.getPartyId(), partyId)) 25 | throw new OcpiGeneralClientException(); 26 | 27 | if (!Objects.equals(object.getUid(), uid)) { 28 | throw new OcpiInvalidParametersException(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ParkingType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum ParkingType { 8 | /** 9 | * Location on a parking facility/rest area along a motorway, freeway, interstate, highway etc. 10 | */ 11 | ALONG_MOTORWAY("ALONG_MOTORWAY"), 12 | /** 13 | * Multistorey car park. 14 | */ 15 | PARKING_GARAGE("PARKING_GARAGE"), 16 | /** 17 | * A cleared area that is intended for parking vehicles, i.e. at super markets, bars, etc. 18 | */ 19 | PARKING_LOT("PARKING_LOT"), 20 | /** 21 | * Location is on the driveway of a house/building. 22 | */ 23 | ON_DRIVEWAY("ON_DRIVEWAY"), 24 | /** 25 | * Parking in public space along a street. 26 | */ 27 | ON_STREET("ON_STREET"), 28 | /** 29 | * Multistorey car park, mainly underground. 30 | */ 31 | UNDERGROUND_GARAGE("UNDERGROUND_GARAGE"); 32 | private final String value; 33 | 34 | ParkingType(String value) { 35 | this.value = value; 36 | } 37 | 38 | @JsonCreator 39 | public static ParkingType fromValue(String value) { 40 | return EnumUtil.findByField( 41 | ParkingType.class, 42 | ParkingType::value, 43 | value 44 | ); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return this.value; 50 | } 51 | 52 | @JsonValue 53 | public String value() { 54 | return this.value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/AuthMethod.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | /** 9 | * Method used for authentication. Multiple AuthMethods are possible during 10 | * a charging sessions, for example when the session was started with a reservation: ReserveNow: COMMAND. 11 | * When the driver arrives and starts charging using a Token that is whitelisted: WHITELIST. 12 | * The last method SHALL be used in the CDR. 13 | */ 14 | public enum AuthMethod { 15 | /** 16 | * Authentication request has been sent to the eMSP. 17 | */ 18 | AUTH_REQUEST("AUTH_REQUEST"), 19 | /** 20 | * Command like StartSession or ReserveNow used to start the Session, the Token provided in the Command was 21 | * used as authorization. 22 | */ 23 | COMMAND("COMMAND"), 24 | /** 25 | * Whitelist used for authentication, no request to the eMSP has been performed. 26 | */ 27 | WHITELIST("WHITELIST"); 28 | private final String value; 29 | 30 | AuthMethod(String value) { 31 | this.value = value; 32 | } 33 | 34 | @JsonCreator 35 | public static AuthMethod fromValue(String value) { 36 | return findByField( 37 | AuthMethod.class, 38 | AuthMethod::value, 39 | value 40 | ); 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | return this.value; 46 | } 47 | 48 | @JsonValue 49 | public String value() { 50 | return this.value; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ParkingRestriction.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 4 | import com.extrawest.ocpi.util.EnumUtil; 5 | import com.fasterxml.jackson.annotation.JsonCreator; 6 | import com.fasterxml.jackson.annotation.JsonValue; 7 | 8 | /** 9 | * This value, if provided, represents the restriction to the parking spot for different purposes. 10 | */ 11 | public enum ParkingRestriction implements OcpiResponseData { 12 | /** 13 | * Reserved parking spot for electric vehicles. 14 | */ 15 | EV_ONLY("EV_ONLY"), 16 | /** 17 | * Reserved parking spot for electric vehicles. 18 | */ 19 | PLUGGED("PLUGGED"), 20 | /** 21 | * Reserved parking spot for electric vehicles. 22 | */ 23 | DISABLED("DISABLED"), 24 | /** 25 | * Reserved parking spot for electric vehicles. 26 | */ 27 | CUSTOMERS("CUSTOMERS"), 28 | /** 29 | * Parking spot only suitable for (electric) motorcycles or scooters. 30 | */ 31 | MOTORCYCLES("MOTORCYCLES"); 32 | private final String value; 33 | 34 | ParkingRestriction(String value) { 35 | this.value = value; 36 | } 37 | 38 | @JsonCreator 39 | public static ParkingRestriction fromValue(String value) { 40 | return EnumUtil.findByField( 41 | ParkingRestriction.class, 42 | ParkingRestriction::value, 43 | value 44 | ); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return this.value; 50 | } 51 | 52 | @JsonValue 53 | public String value() { 54 | return this.value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/EnergySourceCategory.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | public enum EnergySourceCategory { 9 | /** 10 | * Nuclear power sources. 11 | */ 12 | NUCLEAR("NUCLEAR"), 13 | /** 14 | * All kinds of fossil power sources. 15 | */ 16 | GENERAL_FOSSIL("GENERAL_FOSSIL"), 17 | /** 18 | * Fossil power from coal. 19 | */ 20 | COAL("COAL"), 21 | /** 22 | * Fossil power from gas. 23 | */ 24 | GAS("GAS"), 25 | /** 26 | * All kinds of regenerative power sources. 27 | */ 28 | GENERAL_GREEN("GENERAL_GREEN"), 29 | /** 30 | * Regenerative power from PV. 31 | */ 32 | SOLAR("SOLAR"), 33 | /** 34 | * Regenerative power from wind turbines. 35 | */ 36 | WIND("WIND"), 37 | /** 38 | * Regenerative power from water turbines. 39 | */ 40 | WATER("WATER"); 41 | private final String value; 42 | 43 | EnergySourceCategory(String value) { 44 | this.value = value; 45 | } 46 | 47 | @JsonCreator 48 | public static EnergySourceCategory fromValue(String value) { 49 | return findByField( 50 | EnergySourceCategory.class, 51 | EnergySourceCategory::value, 52 | value 53 | ); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return this.value; 59 | } 60 | 61 | @JsonValue 62 | public String value() { 63 | return this.value; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/Image.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.enums.ImageCategory; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.Max; 8 | import jakarta.validation.constraints.NotBlank; 9 | import jakarta.validation.constraints.NotNull; 10 | import jakarta.validation.constraints.Size; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | public class Image implements OcpiResponseData, OcpiRequestData { 17 | 18 | /** 19 | * URL from where the image data can be fetched through a web browser. 20 | */ 21 | @NotBlank 22 | @JsonProperty("url") 23 | private String url; 24 | /** 25 | * URL from where a thumbnail of the image can be fetched through a 26 | * webbrowser. 27 | */ 28 | @JsonProperty("thumbnail") 29 | private String thumbnail; 30 | /** 31 | * Describes what the image is used for. 32 | */ 33 | @NotNull 34 | @JsonProperty("category") 35 | private ImageCategory category; 36 | /** 37 | * Image type like: gif, jpeg, png, svg. 38 | */ 39 | @NotBlank 40 | @Size(max = 4) 41 | @JsonProperty("type") 42 | private String type; 43 | /** 44 | * Width of the full scale image. 45 | */ 46 | @Max(5) 47 | @JsonProperty("width") 48 | private String width; 49 | /** 50 | * Height of the full scale image. 51 | */ 52 | @Max(5) 53 | @JsonProperty("height") 54 | private String height; 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/CommandType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * The command requested. 9 | */ 10 | public enum CommandType { 11 | /** 12 | * Request the Charge Point to cancel a specific reservation. 13 | */ 14 | CANCEL_RESERVATION("CANCEL_RESERVATION"), 15 | /** 16 | * Request the Charge Point to reserve a (specific) EVSE for a Token for a certain time, starting now. 17 | */ 18 | RESERVE_NOW("RESERVE_NOW"), 19 | /** 20 | * Request the Charge Point to start a transaction on the given EVSE/Connector. 21 | */ 22 | START_SESSION("START_SESSION"), 23 | /** 24 | * Request the Charge Point to stop an ongoing session. 25 | */ 26 | STOP_SESSION("STOP_SESSION"), 27 | /** 28 | * Request the Charge Point to unlock the connector (if applicable). 29 | * This functionality is for help desk operators only! 30 | */ 31 | UNLOCK_CONNECTOR("UNLOCK_CONNECTOR"); 32 | private final String value; 33 | 34 | CommandType(String value) { 35 | this.value = value; 36 | } 37 | 38 | @JsonCreator 39 | public static CommandType fromValue(String value) { 40 | return EnumUtil.findByField( 41 | CommandType.class, 42 | CommandType::value, 43 | value 44 | ); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return this.value; 50 | } 51 | 52 | @JsonValue 53 | public String value() { 54 | return this.value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/ClientOwnedObject.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 4 | import com.extrawest.ocpi.util.Constants; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotNull; 8 | import jakarta.validation.constraints.Pattern; 9 | import jakarta.validation.constraints.Size; 10 | import lombok.Getter; 11 | import lombok.Setter; 12 | 13 | import java.time.LocalDateTime; 14 | 15 | @Getter 16 | @Setter 17 | public class ClientOwnedObject implements OcpiResponseData { 18 | /** 19 | * ISO-3166 alpha-2 country code of the CPO that 'owns' this object (e.g. Session, Tariff, Cdr). 20 | */ 21 | @NotBlank 22 | @Size(min = 1, max = 2) 23 | @Pattern(regexp = Constants.ASCII_REGEXP) 24 | @JsonProperty("country_code") 25 | private String countryCode; 26 | 27 | /** 28 | * ID of the CPO that 'owns' this object (e.g. Session, Tariff, Cdr) (following the ISO-15118 standard). 29 | */ 30 | @NotBlank 31 | @Size(min = 1, max = 3) 32 | @Pattern(regexp = Constants.ASCII_REGEXP) 33 | @JsonProperty("party_id") 34 | private String partyId; 35 | 36 | /** 37 | * The unique id that identifies the object in the CPO/eMSP platform. 38 | */ 39 | @NotBlank 40 | @Size(min = 1, max = 36) 41 | @Pattern(regexp = Constants.ASCII_REGEXP) 42 | private String id; 43 | 44 | /** 45 | * Timestamp when this object (e.g. Session, Tariff, Cdr) was last updated (or created). 46 | */ 47 | @NotNull 48 | @JsonProperty("last_updated") 49 | private LocalDateTime lastUpdated; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/tariff/PriceComponent.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.tariff; 2 | 3 | import com.extrawest.ocpi.model.enums.TariffDimensionType; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import jakarta.validation.constraints.Digits; 7 | import jakarta.validation.constraints.NotNull; 8 | import jakarta.validation.constraints.Positive; 9 | import lombok.*; 10 | 11 | 12 | @Getter 13 | @Setter 14 | @ToString 15 | @EqualsAndHashCode 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class PriceComponent implements OcpiRequestData, OcpiResponseData { 19 | /** 20 | * Type of tariff dimension. 21 | */ 22 | @NotNull 23 | private TariffDimensionType type; 24 | /** 25 | * Price per unit (excl. VAT) for this tariff dimension. 26 | */ 27 | @NotNull 28 | @Positive 29 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 30 | private Float price; 31 | /** 32 | * Applicable VAT percentage for this tariff dimension. If omitted, no VAT is applicable. 33 | * Not providing a VAT is different from 0% VAT, which would be a value of 0.0 here. 34 | */ 35 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 36 | private Float vat; 37 | /** 38 | * Minimum amount to be billed. This unit will be billed in this step_size blocks. 39 | * Amounts that are less than this step_size are rounded up to the given step_size. 40 | * For example: if type is TIME and step_size has a value of 300, then time will be billed in blocks of 5 minutes. 41 | * If 6 minutes were used, 10 minutes (2 blocks of step_size) will be billed. 42 | */ 43 | @Positive 44 | private int step_size; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/PublishTokenType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.enums.TokenType; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.Size; 7 | import lombok.*; 8 | 9 | /** 10 | * Defines the set of values that identify a token to which a Location might be published. 11 | * At least one of the following fields SHALL be set: uid, visual_number, or group_id. 12 | * When uid is set, type SHALL also be set. 13 | * When visual_number is set, issuer SHALL also be set. 14 | */ 15 | @Getter 16 | @ToString 17 | @EqualsAndHashCode(callSuper = false) 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class PublishTokenType implements OcpiRequestData { 21 | 22 | /** 23 | * Unique ID by which this Token can be identified. 24 | */ 25 | @Size(max = 36) 26 | @JsonProperty("uid") 27 | private String uid; 28 | /** 29 | * Type of the token. 30 | */ 31 | @JsonProperty("type") 32 | private TokenType type; 33 | /** 34 | * Visual readable number/identification as printed on the Token (RFID card). 35 | */ 36 | @Size(max = 64) 37 | @JsonProperty("visual_number") 38 | private String visualNumber; 39 | /** 40 | * Issuing company, most of the time the name of the company printed on the token (RFID card), 41 | * not necessarily the eMSP 42 | */ 43 | @Size(max = 64) 44 | @JsonProperty("issuer") 45 | private String issuer; 46 | /** 47 | * This ID groups a couple of tokens. This can be used to make two or more tokens work as one. 48 | */ 49 | @Size(max = 36) 50 | @JsonProperty("group_id") 51 | private String groupId; 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/TariffType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum TariffType { 8 | /** 9 | * Used to describe that a Tariff is valid when ad-hoc payment is used at the Charge Point (for example: 10 | * Debit or Credit card payment terminal) 11 | */ 12 | AD_HOC_PAYMENT("AD_HOC_PAYMENT"), 13 | /** 14 | * Used to describe that a Tariff is valid when Charging Preference: CHEAP is set for the session. 15 | */ 16 | PROFILE_CHEAP("PROFILE_CHEAP"), 17 | /** 18 | * Used to describe that a Tariff is valid when Charging Preference: FAST is set for the session. 19 | */ 20 | PROFILE_FAST("PROFILE_FAST"), 21 | /** 22 | * Used to describe that a Tariff is valid when Charging Preference: GREEN is set for the session. 23 | */ 24 | REGULAR("REGULAR"), 25 | /** 26 | * Used to describe that a Tariff is valid when using an RFID, without any Charging Preference, or 27 | * when Charging Preference: REGULAR is set for the session. 28 | */ 29 | PROFILE_GREEN("PROFILE_GREEN"); 30 | private final String value; 31 | 32 | TariffType(String value) { 33 | this.value = value; 34 | } 35 | 36 | @JsonCreator 37 | public static TariffType fromValue(String value) { 38 | return EnumUtil.findByField( 39 | TariffType.class, 40 | TariffType::value, 41 | value 42 | ); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return this.value; 48 | } 49 | 50 | @JsonValue 51 | public String value() { 52 | return this.value; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ChargingProfileResponseType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Response to the ChargingProfile request from the eMSP to the CPO. 9 | */ 10 | public enum ChargingProfileResponseType { 11 | /** 12 | * ChargingProfile request accepted by the CPO, request will be forwarded to the EVSE. 13 | */ 14 | ACCEPTED("ACCEPTED"), 15 | /** 16 | * The ChargingProfiles not supported by this CPO, Charge Point, EVSE etc. 17 | */ 18 | NOT_SUPPORTED("NOT_SUPPORTED"), 19 | /** 20 | * ChargingProfile request rejected by the CPO. 21 | * (Session might not be from a customer of the eMSP that send this request) 22 | */ 23 | REJECTED("REJECTED"), 24 | /** 25 | * ChargingProfile request rejected by the CPO, requests are send more often then allowed. 26 | */ 27 | TOO_OFTEN("TOO_OFTEN"), 28 | /** 29 | * The Session in the requested command is not known by this CPO. 30 | */ 31 | UNKNOWN_SESSION("UNKNOWN_SESSION"); 32 | private final String value; 33 | 34 | ChargingProfileResponseType(String value) { 35 | this.value = value; 36 | } 37 | 38 | @JsonCreator 39 | public static ChargingProfileResponseType fromValue(String value) { 40 | return EnumUtil.findByField( 41 | ChargingProfileResponseType.class, 42 | ChargingProfileResponseType::value, 43 | value 44 | ); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return this.value; 50 | } 51 | 52 | @JsonValue 53 | public String value() { 54 | return this.value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ChargingRateUnit.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Unit in which a charging profile is defined. 9 | */ 10 | public enum ChargingRateUnit { 11 | /** 12 | * Watts (power) This is the TOTAL allowed charging power. If used for AC Charging, the phase current should be 13 | * calculated via: Current per phase = Power / (Line Voltage * Number of Phases). The "Line Voltage" used in the 14 | * calculation is the Line to Neutral Voltage (VLN). In Europe and Asia VLN is typically 220V or 230V and the 15 | * corresponding Line to Line Voltage (VLL) is 380V and 400V. The "Number of Phases" is the numberPhases from the 16 | * ChargingProfilePeriod. It is usually more convenient to use this for DC charging. Note that if numberPhases 17 | * in a ChargingProfilePeriod is absent, 3 SHALL be assumed. 18 | */ 19 | W("W"), 20 | /** 21 | * Amperes (current) The amount of Ampere per phase, not the sum of all phases. It is usually more convenient to use 22 | * this for AC charging. 23 | */ 24 | A("A"); 25 | private final String value; 26 | 27 | ChargingRateUnit(String value) { 28 | this.value = value; 29 | } 30 | 31 | @JsonCreator 32 | public static ChargingRateUnit fromValue(String value) { 33 | return EnumUtil.findByField( 34 | ChargingRateUnit.class, 35 | ChargingRateUnit::value, 36 | value 37 | ); 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return this.value; 43 | } 44 | 45 | @JsonValue 46 | public String value() { 47 | return this.value; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/ClientInfoDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.enums.ConnectionStatus; 4 | import com.extrawest.ocpi.model.enums.Role; 5 | import com.extrawest.ocpi.util.Constants; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.NotNull; 9 | import jakarta.validation.constraints.Pattern; 10 | import jakarta.validation.constraints.Size; 11 | import lombok.AllArgsConstructor; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | 15 | import java.time.LocalDateTime; 16 | 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class ClientInfoDto { 21 | /** 22 | * CPO or eMSP ID of this party (following the 15118 ISO standard), as used in the credentials exchange. 23 | */ 24 | @NotBlank 25 | @Size(min = 1, max = 3) 26 | @Pattern(regexp = Constants.ASCII_REGEXP) 27 | @JsonProperty("party_id") 28 | private String partyId; 29 | 30 | /** 31 | * Country code of the country this party is operating in, as used in the credentials exchange. 32 | */ 33 | @NotBlank 34 | @Size(min = 1, max = 2) 35 | @Pattern(regexp = Constants.ASCII_REGEXP) 36 | @JsonProperty("country_code") 37 | private String countryCode; 38 | 39 | /** 40 | * The role of the connected party. 41 | */ 42 | @NotNull 43 | @JsonProperty("role") 44 | private Role role; 45 | 46 | /** 47 | * Status of the connection to the party. 48 | */ 49 | @NotNull 50 | @JsonProperty("status") 51 | private ConnectionStatus status; 52 | 53 | /** 54 | * Timestamp when this ClientInfo object was last updated. 55 | */ 56 | @NotNull 57 | @JsonProperty("last_updated") 58 | private LocalDateTime lastUpdated; 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/Hours.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.NoArgsConstructor; 10 | import lombok.ToString; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * Opening and access hours of the location. 16 | */ 17 | @Getter 18 | @ToString 19 | @EqualsAndHashCode(callSuper = false) 20 | @NoArgsConstructor 21 | public class Hours implements OcpiRequestData, OcpiResponseData { 22 | /** 23 | * True to represent 24 hours a day and 7 days a week, except the given exceptions. 24 | */ 25 | @NotNull 26 | @JsonProperty("twentyfourseven") 27 | private Boolean twentyFourSeven; 28 | /** 29 | * Regular hours, weekday-based. Only to be used if twentyfourseven=false, then this field needs to contain at least 30 | * one RegularHours object. 31 | */ 32 | @JsonProperty("regular_hours") 33 | private List regularHours; 34 | /** 35 | * Exceptions for specified calendar dates, time-range based. Periods the station is operating/accessible. 36 | * Additional to regular_hours. May overlap regular rules. 37 | */ 38 | @JsonProperty("exceptional_openings") 39 | private List exceptionalOpenings; 40 | /** 41 | * Exceptions for specified calendar dates, time-range based. Periods the station is not operating/accessible. 42 | * Overwriting regular_hours and exceptional_openings. Should not overlap exceptional_openings. 43 | */ 44 | @JsonProperty("exceptional_closings") 45 | private List exceptionalClosings; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/WhitelistType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Defines when authorization of a Token by the CPO is allowed. 9 | */ 10 | public enum WhitelistType { 11 | /** 12 | * Token always has to be whitelisted, realtime authorization is not possible/allowed. CPO shall always allow 13 | * any use of this Token. 14 | */ 15 | ALWAYS("ALWAYS"), 16 | /** 17 | * It is allowed to whitelist the token, realtime authorization is also allowed. The CPO may choose which version 18 | * of authorization to use. 19 | */ 20 | ALLOWED("ALLOWED"), 21 | /** 22 | * In normal situations realtime authorization shall be used. But when the CPO cannot get a response from the eMSP 23 | * (communication between CPO and eMSP is offline), the CPO shall allow this Token to be used. 24 | */ 25 | ALLOWED_OFFLINE("ALLOWED_OFFLINE"), 26 | /** 27 | * Whitelisting is forbidden, only realtime authorization is allowed. CPO shall always send a realtime 28 | * authorization for any use of this Token to the eMSP. 29 | */ 30 | NEVER("NEVER"); 31 | private final String value; 32 | 33 | WhitelistType(String value) { 34 | this.value = value; 35 | } 36 | 37 | @JsonCreator 38 | public static WhitelistType fromValue(String value) { 39 | return EnumUtil.findByField( 40 | WhitelistType.class, 41 | WhitelistType::value, 42 | value 43 | ); 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return this.value; 49 | } 50 | 51 | @JsonValue 52 | public String value() { 53 | return this.value; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ChargingPreferencesResponse.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Different smart charging profile types. 9 | */ 10 | public enum ChargingPreferencesResponse { 11 | /** 12 | * Charging Preferences accepted, EVSE will try to accomplish them, although this is no guarantee that they will be 13 | * fulfilled. 14 | */ 15 | ACCEPTED("ACCEPTED"), 16 | /** 17 | * CPO requires departure_time to be able to perform Charging Preference based Smart Charging. 18 | */ 19 | DEPARTURE_REQUIRED("DEPARTURE_REQUIRED"), 20 | /** 21 | * CPO requires energy_need to be able to perform Charging Preference based Smart Charging. 22 | */ 23 | ENERGY_NEED_REQUIRED("ENERGY_NEED_REQUIRED"), 24 | /** 25 | * Charging Preferences contain a demand that the EVSE knows it cannot fulfill. 26 | */ 27 | NOT_POSSIBLE("NOT_POSSIBLE"), 28 | /** 29 | * profile_type contains a value that is not supported by the EVSE. 30 | */ 31 | PROFILE_TYPE_NOT_SUPPORTED("PROFILE_TYPE_NOT_SUPPORTED"); 32 | private final String value; 33 | 34 | ChargingPreferencesResponse(String value) { 35 | this.value = value; 36 | } 37 | 38 | @JsonCreator 39 | public static ChargingPreferencesResponse fromValue(String value) { 40 | return EnumUtil.findByField( 41 | ChargingPreferencesResponse.class, 42 | ChargingPreferencesResponse::value, 43 | value 44 | ); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return this.value; 50 | } 51 | 52 | @JsonValue 53 | public String value() { 54 | return this.value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/config/OpenAPIConfig.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.config; 2 | 3 | import io.swagger.v3.oas.models.OpenAPI; 4 | import io.swagger.v3.oas.models.info.Contact; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import io.swagger.v3.oas.models.info.License; 7 | import io.swagger.v3.oas.models.servers.Server; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | import java.util.List; 13 | 14 | @Configuration 15 | public class OpenAPIConfig { 16 | 17 | @Value("${extrawest.openapi.dev-url}") 18 | private String devUrl; 19 | 20 | @Value("${extrawest.openapi.prod-url}") 21 | private String prodUrl; 22 | 23 | @Bean 24 | public OpenAPI myOpenAPI() { 25 | Server devServer = new Server(); 26 | devServer.setUrl(devUrl); 27 | devServer.setDescription("Server URL in Development environment"); 28 | 29 | Server prodServer = new Server(); 30 | prodServer.setUrl(prodUrl); 31 | prodServer.setDescription("Server URL in Production environment"); 32 | 33 | Contact contact = new Contact(); 34 | contact.setEmail("ocpi@extrawest.com"); 35 | contact.setName("Extrawest"); 36 | contact.setUrl("https://extrawest.com"); 37 | 38 | License mitLicense = new License().name("MIT License").url("https://choosealicense.com/licenses/mit/"); 39 | 40 | Info info = new Info() 41 | .title("OCPI 2.2.1") 42 | .version("1.0") 43 | .contact(contact) 44 | .description("This API exposes endpoints to manage OCPI entities.").termsOfService("https://extrawest.com") 45 | .license(mitLicense); 46 | 47 | return new OpenAPI().info(info).servers(List.of(devServer, prodServer)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/ChargingPreferences.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.dto.location.Connector; 4 | import com.extrawest.ocpi.model.enums.ProfileType; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.time.LocalDateTime; 12 | 13 | /** 14 | * Contains the charging preferences of an EV driver. 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class ChargingPreferences { 20 | /** 21 | * Type of Smart Charging Profile selected by the driver. The ProfileType has to be supported at the Connector 22 | * {@link Connector} and for every supported ProfileType, a Tariff MUST be provided. 23 | * This gives the EV driver the option between different pricing options. 24 | */ 25 | @NotNull 26 | @JsonProperty("profile_type") 27 | private ProfileType profileType; 28 | /** 29 | * Expected departure. The driver has given this Date/Time as expected departure moment. It is only an estimation 30 | * and not necessarily the Date/Time of the actual departure. 31 | */ 32 | @JsonProperty("departure_time") 33 | private LocalDateTime departureTime; 34 | /** 35 | * Requested amount of energy in kWh. The EV driver wants to have this amount of energy charged. 36 | */ 37 | @JsonProperty("energy_need") 38 | private Float energyNeed; 39 | /** 40 | * The driver allows their EV to be discharged when needed, as long as the other preferences are met: EV is charged 41 | * with the preferred energy (energy_need) until the preferred departure moment (departure_time). 42 | * Default if omitted: false 43 | */ 44 | @JsonProperty("discharge_allowed") 45 | private Boolean dischargeAllowed = false; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/ChargingPeriod.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 8 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 9 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 10 | import jakarta.validation.constraints.NotNull; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | import javax.validation.constraints.NotEmpty; 15 | import java.time.LocalDateTime; 16 | import java.util.List; 17 | 18 | /** 19 | * A Charging Period consists of a start timestamp and a list of possible values that influence this period, 20 | * for example: amount of energy charged this period, maximum current during this period etc. 21 | */ 22 | @Data 23 | @NoArgsConstructor 24 | public class ChargingPeriod implements OcpiRequestData, OcpiResponseData { 25 | 26 | /** 27 | * Start timestamp of the charging period. A period ends when the next period starts. 28 | * The last period ends when the session ends. 29 | */ 30 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 31 | @JsonSerialize(using = LocalDateTimeSerializer.class) 32 | @JsonProperty("start_date_time") 33 | @NotNull 34 | private LocalDateTime startDateTime; 35 | 36 | /** 37 | * List of relevant values for this charging period. 38 | */ 39 | @NotEmpty 40 | @JsonProperty("dimensions") 41 | private List dimensions; 42 | 43 | /** 44 | * Unique identifier of the Tariff that is relevant for this Charging Period. If not provided, 45 | * no Tariff is relevant during this period. 46 | */ 47 | @JsonProperty("tariff_id") 48 | private String tariffId; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/SessionStatus.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Defines the state of a session. 9 | */ 10 | public enum SessionStatus { 11 | /** 12 | * The session has been accepted and is active. All pre-conditions were met: Communication between EV and EVSE 13 | * (for example: cable plugged in correctly), EV or driver is authorized. EV is being charged, or can be charged. 14 | * Energy is, or is not, being transfered. 15 | */ 16 | ACTIVE("ACTIVE"), 17 | /** 18 | * The session has been finished successfully. No more modifications will be made to the Session object using this state. 19 | */ 20 | COMPLETED("COMPLETED"), 21 | /** 22 | * The Session object using this state is declared invalid and will not be billed. 23 | */ 24 | INVALID("INVALID"), 25 | /** 26 | * The session is pending, it has not yet started. Not all pre-conditions are met. This is the initial state. 27 | * The session might never become an active session. 28 | */ 29 | PENDING("PENDING"), 30 | /** 31 | * The session is started due to a reservation, charging has not yet started. The session might never become 32 | * an active session. 33 | */ 34 | RESERVATION("RESERVATION"); 35 | private final String value; 36 | 37 | SessionStatus(String value) { 38 | this.value = value; 39 | } 40 | 41 | @JsonCreator 42 | public static SessionStatus fromValue(String value) { 43 | return EnumUtil.findByField( 44 | SessionStatus.class, 45 | SessionStatus::value, 46 | value 47 | ); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return this.value; 53 | } 54 | 55 | @JsonValue 56 | public String value() { 57 | return this.value; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/cdr/SignedData.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.cdr; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 4 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotEmpty; 8 | import jakarta.validation.constraints.Size; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.Getter; 11 | import lombok.NoArgsConstructor; 12 | import lombok.ToString; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * This class contains all the information of the signed data. Which encoding method is used, if needed, 18 | * the public key and a list of signed values. 19 | */ 20 | @Getter 21 | @ToString 22 | @EqualsAndHashCode(callSuper = false) 23 | @NoArgsConstructor 24 | public class SignedData implements OcpiRequestData, OcpiResponseData { 25 | 26 | /** 27 | * The name of the encoding used in the SignedData field. This is the name given to the encoding by a company 28 | * or group of companies. See note below. 29 | */ 30 | @NotBlank 31 | @Size(max = 36) 32 | @JsonProperty("encoding_method") 33 | private String encodingMethod; 34 | 35 | /** 36 | * Version of the EncodingMethod (when applicable) 37 | */ 38 | @JsonProperty("encoding_method_version") 39 | private Integer encodingMethodVersion; 40 | 41 | /** 42 | * Public key used to sign the data, base64 encoded. 43 | */ 44 | @Size(max = 512) 45 | @JsonProperty("public_key") 46 | private String publicKey; 47 | 48 | /** 49 | * One or more signed values 50 | */ 51 | @NotEmpty 52 | @JsonProperty("signed_values") 53 | private List signedValues; 54 | 55 | /** 56 | * URL that can be shown to an EV driver. This URL gives the EV driver the possibility to check 57 | * the signed data from a charging session. 58 | */ 59 | @Size(max = 512) 60 | @JsonProperty("url") 61 | private String url; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/charging_profile/ChargingProfile.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.charging_profile; 2 | 3 | import com.extrawest.ocpi.model.enums.ChargingRateUnit; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.constraints.Digits; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.time.LocalDateTime; 11 | import java.util.List; 12 | 13 | /** 14 | * Charging profile class defines a list of charging periods. 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | public class ChargingProfile { 19 | 20 | /** 21 | * Starting point of an absolute profile. If absent the profile will be relative to start of charging. 22 | */ 23 | @JsonProperty("start_date_time") 24 | private LocalDateTime startDateTime; 25 | 26 | /** 27 | * Duration of the charging profile in seconds. If the duration is left empty, the last period will continue 28 | * indefinitely or until end of the transaction in case start_date_time is absent. 29 | */ 30 | @JsonProperty("duration") 31 | private Integer duration; 32 | 33 | /** 34 | * The unit of measure. 35 | */ 36 | @NotNull 37 | @JsonProperty("charging_rate_unit") 38 | private ChargingRateUnit chargingRateUnit; 39 | 40 | /** 41 | * Minimum charging rate supported by the EV. The unit of measure is defined by the chargingRateUnit. This parameter 42 | * is intended to be used by a local smart charging algorithm to optimize the power allocation for in the case 43 | * a charging process is inefficient at lower charging rates. Accepts at most one digit fraction (e.g. 8.1) 44 | */ 45 | @JsonProperty("min_charging_rate") 46 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 47 | private Float minChargingRate; 48 | 49 | /** 50 | * List of ChargingProfilePeriod elements defining maximum power or current usage over time. 51 | */ 52 | @JsonProperty("charging_profile_period") 53 | private List chargingProfilePeriod; 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/ImageCategory.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * The category of an image to obtain the correct usage in a user presentation. The category has 9 | * to be set accordingly to the image content in order to guarantee the right usage. 10 | */ 11 | public enum ImageCategory { 12 | /** 13 | * Photo of the physical device that contains one or more EVSEs. 14 | */ 15 | CHARGER("CHARGER"), 16 | /** 17 | * Location entrance photo. Should show the car entrance to the location from street side. 18 | */ 19 | ENTRANCE("ENTRANCE"), 20 | /** 21 | * Location overview photo. 22 | */ 23 | LOCATION("LOCATION"), 24 | /** 25 | * Logo of an associated roaming network to be displayed with the EVSE for example in lists, maps 26 | * and detailed information views. 27 | */ 28 | NETWORK("NETWORK"), 29 | /** 30 | * Logo of an associated roaming network to be displayed with the EVSE for example in lists, maps 31 | * and detailed information views. 32 | */ 33 | OPERATOR("OPERATOR"), 34 | /** 35 | * Other 36 | */ 37 | OTHER("OTHER"), 38 | /** 39 | * Logo of the charge point owner, for example a local store, to be displayed in the EVSEs detailed 40 | * information view 41 | */ 42 | OWNER("OWNER"); 43 | private final String value; 44 | 45 | ImageCategory(String value) { 46 | this.value = value; 47 | } 48 | 49 | @JsonCreator 50 | public static ImageCategory fromValue(String value) { 51 | return EnumUtil.findByField( 52 | ImageCategory.class, 53 | ImageCategory::value, 54 | value 55 | ); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return this.value; 61 | } 62 | 63 | @JsonValue 64 | public String value() { 65 | return this.value; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/service/pagination/PaginationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.service.pagination; 2 | 3 | import com.extrawest.ocpi.model.dto.PaginationHeaders; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.http.HttpHeaders; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.web.util.UriComponentsBuilder; 9 | 10 | @Service 11 | public class PaginationServiceImpl implements PaginationService { 12 | public static final String OCPI_PAGINATION_LINK_HEADER = "<%s>; rel=\"next\""; 13 | @Value("${maxXLimit}") 14 | private String maxXLimit; 15 | 16 | @Override 17 | public int adjustLimitByMax(Integer limit) { 18 | limit = limit == null || limit > Integer.parseInt(maxXLimit) ? Integer.parseInt(maxXLimit) : limit; 19 | return limit; 20 | } 21 | 22 | @Override 23 | public HttpHeaders buildHeader(Integer offset, Integer limit, HttpServletRequest request, long totalCount) { 24 | HttpHeaders responseHeaders = new HttpHeaders(); 25 | if (hasNext(offset, limit, totalCount)) { 26 | String uriForNextPage = constructNextPageUri(request, offset, limit); 27 | responseHeaders.set(PaginationHeaders.LINK, String.format(OCPI_PAGINATION_LINK_HEADER, uriForNextPage)); 28 | } 29 | 30 | responseHeaders.add(PaginationHeaders.X_LIMIT, maxXLimit); 31 | responseHeaders.add(PaginationHeaders.X_TOTAL_COUNT, String.valueOf(totalCount)); 32 | return responseHeaders; 33 | } 34 | 35 | private boolean hasNext(int offset, int limit, long totalCount) { 36 | return offset + limit < totalCount; 37 | } 38 | 39 | private String constructNextPageUri(HttpServletRequest request, int offset, int limit) { 40 | return UriComponentsBuilder 41 | .fromUriString(request.getRequestURL().toString()) 42 | .query(request.getQueryString()) 43 | .replaceQueryParam("offset", offset + limit) 44 | .encode() 45 | .toUriString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/StartSession.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.extrawest.ocpi.model.dto.token.TokenDto; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.NoArgsConstructor; 11 | 12 | /** 13 | * StartSession object, for the START_SESSION command, with information needed to start a sessions. 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @NoArgsConstructor 18 | public class StartSession extends AbstractCommand { 19 | 20 | /** 21 | * Token object the Charge Point has to use to start a new session. The 22 | * Token provided in this request is authorized by the eMSP. 23 | */ 24 | @NotNull 25 | @JsonProperty("token") 26 | private TokenDto token; 27 | 28 | /** 29 | * Location.id of the Location (belonging to the CPO this request is send to) on which a session is to be started. 30 | */ 31 | @NotBlank 32 | @Size(min = 1, max = 36) 33 | @JsonProperty("location_id") 34 | private String locationId; 35 | 36 | /** 37 | * Optional EVSE.uid of the EVSE of this Location on which a session is to be started. Required when connector_id 38 | * is set. 39 | */ 40 | @Size(min = 1, max = 36) 41 | @JsonProperty("evse_uid") 42 | private String evseUid; 43 | 44 | /** 45 | * Optional Connector.id of the Connector of the EVSE on which a session is to be started. This field is required 46 | * when the capability: START_SESSION_CONNECTOR_REQUIRED is set on the EVSE. 47 | */ 48 | @Size(min = 1, max = 36) 49 | @JsonProperty("connector_id ") 50 | private String connectorId; 51 | 52 | /** 53 | * Reference to the authorization given by the eMSP, when given, this reference will be provided in the relevant 54 | * Session and/or CDR. 55 | */ 56 | @Size(min = 1, max = 36) 57 | @JsonProperty("authorization_reference") 58 | private String authorizationReference; 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/AuthorizationInfoDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.dto.cdr.CDRDto; 4 | import com.extrawest.ocpi.model.dto.token.TokenDto; 5 | import com.extrawest.ocpi.model.enums.AllowedType; 6 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 7 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import jakarta.validation.constraints.NotNull; 10 | import jakarta.validation.constraints.Size; 11 | import lombok.AllArgsConstructor; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | 15 | /** 16 | * Contains information about the authorization, if the Token is allowed to charge and optionally which 17 | * EVSEs are allowed to be used. 18 | */ 19 | @Data 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | public class AuthorizationInfoDto implements OcpiRequestData, OcpiResponseData { 23 | 24 | /** 25 | * Status of the Token, and whether charging is allowed at the optionally given location. 26 | */ 27 | @NotNull 28 | @JsonProperty("allowed") 29 | private AllowedType allowed; 30 | 31 | /** 32 | * The complete Token object for which this authorization was requested. 33 | */ 34 | @NotNull 35 | @JsonProperty("token") 36 | private TokenDto token; 37 | 38 | /** 39 | * Optional reference to the location if it was included in the request, and if the EV driver is allowed to charge 40 | * at that location. Only the EVSEs the EV driver is allowed to charge at are returned. 41 | */ 42 | @JsonProperty("location") 43 | private LocationReferencesDto location; 44 | 45 | /** 46 | * Reference to the authorization given by the eMSP, when given, this reference will be provided in the relevant 47 | * {@link SessionDto} and/or {@link CDRDto}. 48 | */ 49 | @Size(min = 1, max = 36) 50 | @JsonProperty("authorization_reference") 51 | private String authorizationReference; 52 | 53 | /** 54 | * Optional display text, additional information to the EV driver. 55 | */ 56 | @JsonProperty("info") 57 | private DisplayText info; 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/CommandResultType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | /** 8 | * Result of the command that was sent to the Charge Point. 9 | */ 10 | public enum CommandResultType { 11 | /** 12 | * Command request accepted by the Charge Point. 13 | */ 14 | ACCEPTED("ACCEPTED"), 15 | /** 16 | * The Reservation has been canceled by the CPO. 17 | */ 18 | CANCELED_RESERVATION("CANCELED_RESERVATION"), 19 | /** 20 | * EVSE is currently occupied, another session is ongoing. Cannot start a new session 21 | */ 22 | EVSE_OCCUPIED("EVSE_OCCUPIED"), 23 | /** 24 | * EVSE is currently inoperative or faulted. 25 | */ 26 | EVSE_INOPERATIVE("EVSE_INOPERATIVE"), 27 | /** 28 | * Execution of the command failed at the Charge Point. 29 | */ 30 | FAILED("FAILED"), 31 | /** 32 | * The requested command is not supported by this Charge Point, EVSE etc. 33 | */ 34 | NOT_SUPPORTED("NOT_SUPPORTED"), 35 | /** 36 | * Command request rejected by the Charge Point. 37 | */ 38 | REJECTED("REJECTED"), 39 | /** 40 | * Command request timeout, no response received from the Charge Point in a reasonable time. 41 | */ 42 | TIMEOUT("TIMEOUT"), 43 | /** 44 | * The Reservation in the requested command is not known by this Charge Point. 45 | */ 46 | UNKNOWN_RESERVATION("UNKNOWN_RESERVATION"); 47 | private final String value; 48 | 49 | CommandResultType(String value) { 50 | this.value = value; 51 | } 52 | 53 | @JsonCreator 54 | public static CommandResultType fromValue(String value) { 55 | return EnumUtil.findByField( 56 | CommandResultType.class, 57 | CommandResultType::value, 58 | value 59 | ); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return this.value; 65 | } 66 | 67 | @JsonValue 68 | public String value() { 69 | return this.value; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/cdr/CdrToken.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.cdr; 2 | 3 | import com.extrawest.ocpi.model.enums.TokenType; 4 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 5 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.NotNull; 9 | import jakarta.validation.constraints.Size; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | 13 | @Data 14 | @NoArgsConstructor 15 | public class CdrToken implements OcpiRequestData, OcpiResponseData { 16 | /** 17 | * ISO-3166 alpha-2 country code of the MSP that 'owns' this Token. 18 | */ 19 | @JsonProperty("country_code") 20 | @NotBlank 21 | @Size(min = 2, max = 2) 22 | private String countryCode; 23 | /** 24 | * ID of the eMSP that 'owns' this Token (following the ISO-15118 standard). 25 | */ 26 | @NotBlank 27 | @Size(min = 3, max = 3) 28 | @JsonProperty("party_id") 29 | private String partyId; 30 | 31 | /** 32 | * Unique ID by which this Token can be identified. This is the field used by the CPO’s system 33 | * (RFID reader on the Charge Point) to identify this token. Currently, in most cases: type=RFID, 34 | * this is the RFID hidden ID as read by the RFID reader, but that is not a requirement. 35 | * If this is a type=APP_USER Token, it will be a unique, by the eMSP, generated ID. 36 | */ 37 | @JsonProperty("uid") 38 | @NotBlank 39 | @Size(max = 36) 40 | private String uid; 41 | 42 | /** 43 | * Type of the token 44 | */ 45 | @JsonProperty("type") 46 | @NotNull 47 | private TokenType type; 48 | 49 | /** 50 | * Uniquely identifies the EV driver contract token within the eMSP’s platform (and suboperator platforms). 51 | * Recommended to follow the specification for eMA ID from "eMI3 standard version 52 | * V1.0" (...) "Part 2: business objects." 53 | */ 54 | @JsonProperty("contract_id") 55 | @NotBlank 56 | @Size(max = 36) 57 | private String contractId; 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/status_codes/OcpiStatusCode.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums.status_codes; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public enum OcpiStatusCode { 7 | /** 8 | * {@code 1000 Success}. 9 | * 10 | * @see : 1xxx: Success 11 | */ 12 | SUCCESS(1000, "Success"), 13 | 14 | /** 15 | * {@code 2000 Client errors}. 16 | * 17 | * @see : 2xxx: Client errors 18 | */ 19 | CLIENT_ERROR(2000, "Generic client error"), 20 | INVALID_PARAMETERS(2001, "Invalid or missing parameters"), 21 | NOT_ENOUGH_INFORMATION(2002, "Not enough information"), 22 | UNKNOWN_LOCATION(2003, "Unknown Location"), 23 | UNKNOWN_TOKEN(2004, "Unknown Token"), 24 | 25 | /** 26 | * {@code 3000 Server errors}. 27 | * 28 | * @see : 3xxx: Server errors 29 | */ 30 | SERVER_ERROR(3000, "Generic server error"), 31 | UNABLE_TO_USE_API_ERROR(3001, "Unable to use the client’s API"), 32 | UNSUPPORTED_VERSION_ERROR(3002, "Unsupported version"), 33 | MISSION_ENDPOINT_ERROR(3003, "No matching endpoints or expected endpoints missing between parties"), 34 | 35 | /** 36 | * {@code 4000 Hub errors}. 37 | * 38 | * @see : 4xxx: Hub errors 39 | */ 40 | HUB_ERROR(4000, "Generic hub error"), 41 | UNKNOWN_RECEIVER_HUB_ERROR(4001, "Unknown receiver (TO address is unknown)"), 42 | TIMED_OUT_FORWARDED_REQUEST_HUB_ERROR(4002, "Timeout on forwarded request (message is forwarded, but request times out)"), 43 | CONNECTION_PROBLEM_HUB_ERROR(4003, "Connection problem (receiving party is not connected)"); 44 | 45 | private final int value; 46 | private final String reasonPhrase; 47 | 48 | OcpiStatusCode(int value, String reasonPhrase) { 49 | this.value = value; 50 | this.reasonPhrase = reasonPhrase; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/ClientInfoController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ClientInfoDto; 4 | import com.extrawest.ocpi.service.ClientInfoService; 5 | import io.swagger.v3.oas.annotations.tags.Tag; 6 | import jakarta.validation.constraints.Size; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | @RestController 12 | @RequestMapping("/api/2.2.1/hubClientInfo") 13 | @Tag(name = "ClientInfo") 14 | public class ClientInfoController { 15 | 16 | protected final ClientInfoService clientInfoService; 17 | 18 | protected ClientInfoController(@Autowired ClientInfoService clientInfoService) { 19 | this.clientInfoService = clientInfoService; 20 | } 21 | 22 | /** 23 | * Retrieve a ClientInfo object as it is stored in the connected client's system. 24 | * 25 | * @param countryCode Country code of the requested ClientInfo object. 26 | * @param partyId Party ID (Provider ID) of the requested ClientInfo object. 27 | * @return The requested ClientInfo object. 28 | */ 29 | @GetMapping("/{country_code}/{party_id}") 30 | public ResponseEntity getHubClientInfo( 31 | @PathVariable(value = "country_code") @Size(min = 2, max = 2) String countryCode, 32 | @PathVariable(value = "party_id") @Size(min = 3, max = 3) String partyId) { 33 | return ResponseEntity.ok(clientInfoService.getHubClientInfo(countryCode, partyId)); 34 | } 35 | 36 | /** 37 | * Push new/updated ClientInfo object to the connect client. 38 | * 39 | * @param countryCode Country code of the eMSP sending this PUT request to the CPO system. 40 | * @param partyId Party ID (Provider ID) of the eMSP sending this PUT request to the CPO system. 41 | */ 42 | @PutMapping("/{country_code}/{party_id}") 43 | public void putHubClientInfo( 44 | @PathVariable(value = "country_code") @Size(min = 2, max = 2) String countryCode, 45 | @PathVariable(value = "party_id") @Size(min = 3, max = 3) String partyId) { 46 | clientInfoService.putHubClientInfo(countryCode, partyId); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/ResponseFormat.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.constraints.Max; 6 | import jakarta.validation.constraints.Min; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.time.LocalDateTime; 13 | 14 | /** 15 | * The content that is sent with all the response messages is an 'application/json' type and contains a JSON object with 16 | * the following properties. 17 | * For errors on the HTTP layer, use HTTP error response codes, including the response format above, 18 | * that contains more details 19 | */ 20 | @Data 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | public class ResponseFormat { 24 | 25 | /** 26 | * OCPI status code, as listed in Status Codes {@link OcpiStatusCode}, indicates how the request was handled. 27 | * To avoid confusion with HTTP codes, OCPI status codes consist of four digits. 28 | */ 29 | @JsonProperty("status_code") 30 | @NotNull 31 | @Min(value = 1000) 32 | @Max(value = 4999) 33 | private Integer statusCode; 34 | 35 | /** 36 | * An optional status message which may help when debugging. 37 | */ 38 | @JsonProperty("status_message") 39 | private String statusMessage; 40 | 41 | /** 42 | * The time this message was generated. 43 | */ 44 | @NotNull 45 | private LocalDateTime timestamp; 46 | 47 | /** 48 | * Contains the actual response data object or list of objects from each request 49 | */ 50 | private T data; 51 | 52 | public ResponseFormat build(OcpiStatusCode ocpiStatusCode) { 53 | this.setTimestamp(LocalDateTime.now()); 54 | this.setStatusCode(ocpiStatusCode.getValue()); 55 | this.setStatusMessage(ocpiStatusCode.getReasonPhrase()); 56 | return this; 57 | } 58 | 59 | public ResponseFormat build(OcpiStatusCode ocpiStatusCode, T data) { 60 | this.setData(data); 61 | this.setTimestamp(LocalDateTime.now()); 62 | this.setStatusCode(ocpiStatusCode.getValue()); 63 | this.setStatusMessage(ocpiStatusCode.getReasonPhrase()); 64 | return this; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/CpoCommandsController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ResponseFormat; 4 | import com.extrawest.ocpi.model.dto.command.AbstractCommand; 5 | import com.extrawest.ocpi.model.dto.command.CommandResponse; 6 | import com.extrawest.ocpi.model.enums.CommandType; 7 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 8 | import com.extrawest.ocpi.service.CpoCommandsService; 9 | import io.swagger.v3.oas.annotations.tags.Tag; 10 | import jakarta.validation.Valid; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.validation.annotation.Validated; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | @RestController 17 | @RequestMapping("/cpo/api/2.2.1/commands") 18 | @Tag(name = "CPOCommands") 19 | @Validated 20 | public class CpoCommandsController { 21 | 22 | protected final CpoCommandsService cpoCommandsService; 23 | 24 | protected CpoCommandsController(@Autowired CpoCommandsService cpoCommandsService) { 25 | this.cpoCommandsService = cpoCommandsService; 26 | } 27 | 28 | /** 29 | * Send a command to the CPO, requesting the CPO to send the command to the Charge Point 30 | * 31 | * @param command Type of command that is requested. 32 | * @param requestedCommand Depending on the command parameter the body SHALL contain the applicable object 33 | * for that command. 34 | * @return Result of the command request, by the CPO (not the Charge Point). So this indicates if the CPO understood 35 | * the command request and was able to send it to the Charge Point. This is not the response by the Charge Point 36 | */ 37 | @PostMapping("/{command}") 38 | public ResponseEntity> postCommand( 39 | @PathVariable(value = "command") CommandType command, 40 | @RequestBody @Valid AbstractCommand requestedCommand) { 41 | CommandResponse commandResponse = cpoCommandsService.postCommand(command, requestedCommand); 42 | 43 | ResponseFormat responseFormat = new ResponseFormat() 44 | .build(OcpiStatusCode.SUCCESS, commandResponse); 45 | return ResponseEntity.ok(responseFormat); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/security/Role.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.security; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.util.Set; 7 | 8 | import static com.extrawest.ocpi.model.security.Permission.*; 9 | 10 | @AllArgsConstructor 11 | @Getter 12 | public enum Role { 13 | CPO(Set.of(CDRS_SENDER, 14 | CHARGING_PROFILES_RECEIVER, 15 | COMMANDS_RECEIVER, 16 | CREDENTIALS_SENDER, CREDENTIALS_RECEIVER, 17 | HUB_CLIENT_INFO_RECEIVER, 18 | LOCATIONS_SENDER, 19 | SESSIONS_SENDER, 20 | TARIFFS_SENDER, 21 | TOKENS_RECEIVER, 22 | VERSIONS_SENDER, VERSIONS_RECEIVER)), 23 | 24 | EMSP(Set.of(CDRS_RECEIVER, 25 | COMMANDS_SENDER, 26 | CREDENTIALS_SENDER, CREDENTIALS_RECEIVER, 27 | HUB_CLIENT_INFO_RECEIVER, 28 | LOCATIONS_RECEIVER, 29 | SESSIONS_RECEIVER, 30 | TARIFFS_RECEIVER, 31 | TOKENS_SENDER, 32 | VERSIONS_SENDER, VERSIONS_RECEIVER)), 33 | 34 | HUB(Set.of(CDRS_RECEIVER, CDRS_SENDER, 35 | CHARGING_PROFILES_RECEIVER, CHARGING_PROFILES_SENDER, 36 | COMMANDS_RECEIVER, COMMANDS_SENDER, 37 | CREDENTIALS_SENDER, CREDENTIALS_RECEIVER, 38 | HUB_CLIENT_INFO_SENDER, 39 | LOCATIONS_RECEIVER, LOCATIONS_SENDER, 40 | SESSIONS_RECEIVER, SESSIONS_SENDER, 41 | TARIFFS_RECEIVER, TARIFFS_SENDER, 42 | TOKENS_SENDER, TOKENS_RECEIVER, 43 | VERSIONS_SENDER, VERSIONS_RECEIVER)), 44 | 45 | NSP(Set.of(CREDENTIALS_SENDER, CREDENTIALS_RECEIVER, 46 | HUB_CLIENT_INFO_RECEIVER, 47 | LOCATIONS_RECEIVER, 48 | TARIFFS_RECEIVER, 49 | VERSIONS_SENDER, VERSIONS_RECEIVER)), 50 | 51 | NAP(Set.of(CREDENTIALS_SENDER, CREDENTIALS_RECEIVER, 52 | HUB_CLIENT_INFO_RECEIVER, 53 | LOCATIONS_RECEIVER, LOCATIONS_SENDER, 54 | TARIFFS_RECEIVER, TARIFFS_SENDER, 55 | VERSIONS_SENDER, VERSIONS_RECEIVER)), 56 | 57 | SCSP(Set.of(CHARGING_PROFILES_SENDER, 58 | CREDENTIALS_SENDER, CREDENTIALS_RECEIVER, 59 | HUB_CLIENT_INFO_RECEIVER, 60 | SESSIONS_RECEIVER, 61 | VERSIONS_SENDER, VERSIONS_RECEIVER)); 62 | 63 | private final Set permissions; 64 | 65 | } -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/command/ReserveNow.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.command; 2 | 3 | import com.extrawest.ocpi.model.dto.SessionDto; 4 | import com.extrawest.ocpi.model.dto.cdr.CDRDto; 5 | import com.extrawest.ocpi.model.dto.location.EVSE; 6 | import com.extrawest.ocpi.model.dto.token.TokenDto; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | import jakarta.validation.constraints.NotBlank; 9 | import jakarta.validation.constraints.NotNull; 10 | import jakarta.validation.constraints.Size; 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | import lombok.NoArgsConstructor; 14 | 15 | import java.time.LocalDateTime; 16 | 17 | /** 18 | * ReserveNow object, for the RESERVE_NOW command, with information needed to reserve 19 | * a (specific) connector of a Charge Point for a given {@link TokenDto}. 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = true) 23 | @NoArgsConstructor 24 | public class ReserveNow extends AbstractCommand { 25 | 26 | /** 27 | * Token object for how to reserve this Charge Point (and specific EVSE). 28 | */ 29 | @NotNull 30 | @JsonProperty("token") 31 | private TokenDto token; 32 | 33 | /** 34 | * The Date/Time when this reservation ends, in UTC 35 | */ 36 | @NotNull 37 | @JsonProperty("expiry_date") 38 | private LocalDateTime expireDate; 39 | 40 | /** 41 | * Reservation id, unique for this reservation. If the Receiver (typically CPO) Point already has a reservation 42 | * that matches this reservationId for that Location it will replace the reservation. 43 | */ 44 | @NotBlank 45 | @Size(min = 1, max = 36) 46 | @JsonProperty("reservation_id") 47 | private String reservationId; 48 | 49 | /** 50 | * Location.id of the Location (belonging to the CPO this request is send to) for which to reserve an {@link EVSE}. 51 | */ 52 | @NotBlank 53 | @Size(min = 1, max = 36) 54 | @JsonProperty("location_id") 55 | private String locationId; 56 | 57 | /** 58 | * Optional EVSE.uid of the EVSE of this Location if a specific EVSE has to be reserved. 59 | */ 60 | @Size(min = 1, max = 36) 61 | @JsonProperty("evse_uid") 62 | private String evseUid; 63 | 64 | /** 65 | * Reference to the authorization given by the eMSP, when given, this reference will be provided 66 | * in the relevant {@link SessionDto} and/or {@link CDRDto}. 67 | */ 68 | @Size(min = 1, max = 36) 69 | @JsonProperty("authorization_reference") 70 | private String authorizationReference; 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/CpoVersionController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ResponseFormat; 4 | import com.extrawest.ocpi.model.dto.VersionDetailsDto; 5 | import com.extrawest.ocpi.model.dto.VersionDto; 6 | import com.extrawest.ocpi.model.enums.VersionNumber; 7 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 8 | import com.extrawest.ocpi.service.CpoVersionService; 9 | import io.swagger.v3.oas.annotations.tags.Tag; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import java.util.List; 18 | 19 | @RestController 20 | @RequestMapping("/cpo/api/versions") 21 | @Tag(name = "CpoVersion") 22 | public class CpoVersionController { 23 | 24 | protected final CpoVersionService cpoVersionService; 25 | 26 | protected CpoVersionController(@Autowired CpoVersionService cpoVersionService) { 27 | this.cpoVersionService = cpoVersionService; 28 | } 29 | 30 | /** 31 | * Fetch information about the supported versions. 32 | * 33 | * @return list of VersionResponseDTO 34 | */ 35 | @GetMapping 36 | public ResponseEntity>> getVersions() { 37 | List versions = cpoVersionService.getVersions(); 38 | 39 | ResponseFormat> responseFormat = new ResponseFormat>() 40 | .build(OcpiStatusCode.SUCCESS, versions); 41 | return ResponseEntity.ok(responseFormat); 42 | } 43 | 44 | /** 45 | * Via the version details, the parties can exchange which modules are implemented for a specific version of OCPI, 46 | * which interface role is implemented, and what the endpoint URL is for this interface. 47 | * 48 | * @param version - version of OCPI 49 | * @return VersionDetails 50 | */ 51 | @GetMapping("/details") 52 | public ResponseEntity> getVersionDetails( 53 | @RequestParam(value = "version") String version) { 54 | VersionDetailsDto versionDetails = cpoVersionService.getVersionDetails(VersionNumber.fromValue(version)); 55 | 56 | ResponseFormat responseFormat = new ResponseFormat() 57 | .build(OcpiStatusCode.SUCCESS, versionDetails); 58 | return ResponseEntity.ok(responseFormat); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/Facility.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.util.EnumUtil; 4 | import com.fasterxml.jackson.annotation.JsonCreator; 5 | import com.fasterxml.jackson.annotation.JsonValue; 6 | 7 | public enum Facility { 8 | /** 9 | * A hotel. 10 | */ 11 | HOTEL("HOTEL"), 12 | /** 13 | * A restaurant. 14 | */ 15 | RESTAURANT("RESTAURANT"), 16 | /** 17 | * A cafe. 18 | */ 19 | CAFE("CAFE"), 20 | /** 21 | * A mall or shopping center. 22 | */ 23 | MALL("MALL"), 24 | /** 25 | * A mall or shopping center. 26 | */ 27 | SUPERMARKET("SUPERMARKET"), 28 | /** 29 | * Sport facilities: gym, field etc. 30 | */ 31 | SPORT("SPORT"), 32 | /** 33 | * A recreation area. 34 | */ 35 | RECREATION_AREA("RECREATION_AREA"), 36 | /** 37 | * Located in, or close to, a park, nature reserve etc. 38 | */ 39 | NATURE("NATURE"), 40 | /** 41 | * A museum. 42 | */ 43 | MUSEUM("MUSEUM"), 44 | /** 45 | * A bike/e-bike/e-scooter sharing location. 46 | */ 47 | BIKE_SHARING("BIKE_SHARING"), 48 | /** 49 | * A bus stop. 50 | */ 51 | BUS_STOP("BUS_STOP"), 52 | /** 53 | * A taxi stand. 54 | */ 55 | TAXI_STAND("TAXI_STAND"), 56 | /** 57 | * A tram stop/station. 58 | */ 59 | TRAM_STOP("TRAM_STOP"), 60 | /** 61 | * A tram stop/station. 62 | */ 63 | METRO_STATION("METRO_STATION"), 64 | /** 65 | * A tram stop/station. 66 | */ 67 | TRAIN_STATION("TRAIN_STATION"), 68 | /** 69 | * An airport. 70 | */ 71 | AIRPORT("AIRPORT"), 72 | /** 73 | * An airport. 74 | */ 75 | PARKING_LOT("PARKING_LOT"), 76 | /** 77 | * An airport. 78 | */ 79 | CARPOOL_PARKING("CARPOOL_PARKING"), 80 | /** 81 | * An airport. 82 | */ 83 | FUEL_STATION("FUEL_STATION"), 84 | /** 85 | * Wifi or other type of internet available. 86 | */ 87 | WIFI("WIFI"); 88 | private final String value; 89 | 90 | Facility(String value) { 91 | this.value = value; 92 | } 93 | 94 | @JsonCreator 95 | public static Facility fromValue(String value) { 96 | return EnumUtil.findByField( 97 | Facility.class, 98 | Facility::value, 99 | value 100 | ); 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return this.value; 106 | } 107 | 108 | @JsonValue 109 | public String value() { 110 | return this.value; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/Capability.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 4 | import com.extrawest.ocpi.util.EnumUtil; 5 | import com.fasterxml.jackson.annotation.JsonCreator; 6 | import com.fasterxml.jackson.annotation.JsonValue; 7 | 8 | public enum Capability implements OcpiResponseData { 9 | /** 10 | * The EVSE supports charging profiles. 11 | */ 12 | CHARGING_PROFILE_CAPABLE("CHARGING_PROFILE_CAPABLE"), 13 | /** 14 | * The EVSE supports charging preferences. 15 | */ 16 | CHARGING_PREFERENCES_CAPABLE("CHARGING_PREFERENCES_CAPABLE"), 17 | /** 18 | * EVSE has a payment terminal that supports chip cards. 19 | */ 20 | CHIP_CARD_SUPPORT("CHIP_CARD_SUPPORT"), 21 | /** 22 | * EVSE has a payment terminal that supports contactless cards. 23 | */ 24 | CONTACTLESS_CARD_SUPPORT("CONTACTLESS_CARD_SUPPORT"), 25 | /** 26 | * EVSE has a payment terminal that makes it possible to pay for charging using a credit card. 27 | */ 28 | CREDIT_CARD_PAYABLE("CREDIT_CARD_PAYABLE"), 29 | /** 30 | * EVSE has a payment terminal that makes it possible to pay for charging using 31 | * a debit card. 32 | */ 33 | DEBIT_CARD_PAYABLE("DEBIT_CARD_PAYABLE"), 34 | /** 35 | * EVSE has a payment terminal with a pin-code entry device. 36 | */ 37 | PED_TERMINAL("PED_TERMINAL"), 38 | /** 39 | * EVSE has a payment terminal with a pin-code entry device. 40 | */ 41 | REMOTE_START_STOP_CAPABLE("REMOTE_START_STOP_CAPABLE"), 42 | /** 43 | * EVSE has a payment terminal with a pin-code entry device. 44 | */ 45 | RESERVABLE("RESERVABLE"), 46 | /** 47 | * Charging at this EVSE can be authorized with an RFID token. 48 | */ 49 | RFID_READER("RFID_READER"), 50 | /** 51 | * When a StartSession is sent to this EVSE, the MSP is required to add the optional connector_id field 52 | * in the StartSession object. 53 | */ 54 | START_SESSION_CONNECTOR_REQUIRED("START_SESSION_CONNECTOR_REQUIRED"), 55 | /** 56 | * When a StartSession is sent to this EVSE, the MSP is required to add the optional connector_id field in the 57 | * StartSession object. 58 | */ 59 | TOKEN_GROUP_CAPABLE("TOKEN_GROUP_CAPABLE"), 60 | /** 61 | * Connectors have mechanical lock that can be requested by the eMSP to be unlocked. 62 | */ 63 | UNLOCK_CAPABLE("UNLOCK_CAPABLE"); 64 | private final String value; 65 | 66 | Capability(String value) { 67 | this.value = value; 68 | } 69 | 70 | @JsonCreator 71 | public static Capability fromValue(String value) { 72 | return EnumUtil.findByField( 73 | Capability.class, 74 | Capability::value, 75 | value 76 | ); 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return this.value; 82 | } 83 | 84 | @JsonValue 85 | public String value() { 86 | return this.value; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/CpoCdrController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ResponseFormat; 4 | import com.extrawest.ocpi.model.dto.cdr.CDRDto; 5 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 6 | import com.extrawest.ocpi.service.CpoCdrService; 7 | import com.extrawest.ocpi.service.pagination.PaginationService; 8 | import io.swagger.v3.oas.annotations.tags.Tag; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.HttpHeaders; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestParam; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | @RestController 22 | @RequestMapping("/cpo/api/2.2.1/cdr") 23 | @Tag(name = "CPOCdr") 24 | public class CpoCdrController { 25 | 26 | protected final CpoCdrService cpoCdrService; 27 | protected final PaginationService paginationService; 28 | 29 | protected CpoCdrController(@Autowired CpoCdrService cpoCdrService, 30 | @Autowired PaginationService paginationService) { 31 | this.cpoCdrService = cpoCdrService; 32 | this.paginationService = paginationService; 33 | } 34 | 35 | /** 36 | * Fetch CDRs from the CPO’s system. 37 | * 38 | * @param dateFrom Only return CDRs that have last_updated after or equal to this Date/Time (inclusive). 39 | * @param dateTo Only return CDRs that have last_updated up to this Date/Time, but not including (exclusive). 40 | * @param offset The offset of the first object returned. Default is 0. 41 | * @param limit Maximum number of objects to GET. 42 | * @return The endpoint returns a list of CDRs matching the given parameters in the GET request, the header 43 | * will contain the pagination related headers. 44 | */ 45 | @GetMapping 46 | public ResponseEntity>> getCdr( 47 | @RequestParam(value = "date_from") LocalDateTime dateFrom, 48 | @RequestParam(value = "date_to") LocalDateTime dateTo, 49 | @RequestParam(value = "offset") Integer offset, 50 | @RequestParam(value = "limit") Integer limit, 51 | HttpServletRequest request) { 52 | int adjustedLimit = paginationService.adjustLimitByMax(limit); 53 | 54 | List cdrs = cpoCdrService.getCdr(dateFrom, dateTo, offset, limit); 55 | long totalCount = cpoCdrService.getTotalCount(dateFrom, dateTo); 56 | 57 | ResponseFormat> responseFormat = new ResponseFormat>() 58 | .build(OcpiStatusCode.SUCCESS, cdrs); 59 | HttpHeaders responseHeaders = paginationService.buildHeader(offset, adjustedLimit, request, totalCount); 60 | 61 | return ResponseEntity.ok() 62 | .headers(responseHeaders) 63 | .body(responseFormat); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/CpoCredentialsController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.CredentialsDto; 4 | import com.extrawest.ocpi.model.dto.ResponseFormat; 5 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 6 | import com.extrawest.ocpi.service.CpoCredentialsService; 7 | import io.swagger.v3.oas.annotations.tags.Tag; 8 | import jakarta.validation.Valid; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.validation.annotation.Validated; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | @RestController 15 | @RequestMapping("/cpo/api/2.2.1/credentials") 16 | @Tag(name = "CPOCredentials") 17 | @Validated 18 | public class CpoCredentialsController { 19 | 20 | protected final CpoCredentialsService cpoCredentialsService; 21 | 22 | protected CpoCredentialsController(@Autowired CpoCredentialsService cpoCredentialsService) { 23 | this.cpoCredentialsService = cpoCredentialsService; 24 | } 25 | 26 | /** 27 | * Retrieves the credentials object to access the server’s platform. 28 | * 29 | * @return CredentialsDTO 30 | */ 31 | @GetMapping 32 | public ResponseEntity> getCredentials() { 33 | CredentialsDto credentials = cpoCredentialsService.getCredentials(); 34 | 35 | ResponseFormat responseFormat = new ResponseFormat() 36 | .build(OcpiStatusCode.SUCCESS, credentials); 37 | return ResponseEntity.ok(responseFormat); 38 | } 39 | 40 | /** 41 | * Provides the server with a credentials object to access the client’s system (i.e. register). 42 | * 43 | * @param credentialsToClient - credentials to access the client’s system 44 | * @return current client's credentials to access the server’s system with newly generated token 45 | */ 46 | @PostMapping 47 | public ResponseEntity> postCredentials( 48 | @RequestBody @Valid CredentialsDto credentialsToClient) { 49 | 50 | CredentialsDto credentialsToServer = cpoCredentialsService.postCredentials(credentialsToClient); 51 | 52 | ResponseFormat responseFormat = new ResponseFormat() 53 | .build(OcpiStatusCode.SUCCESS, credentialsToServer); 54 | return ResponseEntity.ok(responseFormat); 55 | } 56 | 57 | /** 58 | * Provides the server with an updated credentials object to access the client’s system. 59 | * 60 | * @param credentialsDTO - credentials 61 | */ 62 | @PutMapping 63 | public void putCredentials(@RequestBody @jakarta.validation.Valid CredentialsDto credentialsDTO) { 64 | cpoCredentialsService.putCredentials(credentialsDTO); 65 | } 66 | 67 | /** 68 | * Informs the server that its credentials to the client’s system are now invalid (i.e. unregister). 69 | * 70 | * @param credentialsDTO - credentials 71 | */ 72 | @DeleteMapping 73 | public void deleteCredentials(@RequestBody @Valid CredentialsDto credentialsDTO) { 74 | cpoCredentialsService.deleteCredentials(credentialsDTO); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/CpoTariffController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ResponseFormat; 4 | import com.extrawest.ocpi.model.dto.tariff.TariffDto; 5 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 6 | import com.extrawest.ocpi.service.CpoTariffService; 7 | import com.extrawest.ocpi.service.pagination.PaginationService; 8 | import io.swagger.v3.oas.annotations.tags.Tag; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.HttpHeaders; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.validation.annotation.Validated; 14 | import org.springframework.web.bind.annotation.GetMapping; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | import java.time.LocalDateTime; 20 | import java.util.List; 21 | 22 | @RestController 23 | @RequestMapping("/cpo/api/2.2.1/tariffs") 24 | @Tag(name = "CpoTariff") 25 | @Validated 26 | public class CpoTariffController { 27 | protected final CpoTariffService cpoTariffService; 28 | protected final PaginationService paginationService; 29 | 30 | public CpoTariffController(@Autowired CpoTariffService cpoTariffService, 31 | @Autowired PaginationService paginationService) { 32 | this.cpoTariffService = cpoTariffService; 33 | this.paginationService = paginationService; 34 | } 35 | 36 | /** 37 | * Returns Tariff objects from the CPO, last updated between the {date_from} and {date_to} (paginated) 38 | * 39 | * @param dateFrom Only return Tariffs that have last_updated after or equal to this Date/Time (inclusive). 40 | * @param dateTo Only return Tariffs that have last_updated up to this Date/Time, but not including (exclusive). 41 | * @param offset The offset of the first object returned. Default is 0. 42 | * @param limit Maximum number of objects to GET 43 | * @return List of all tariffs. 44 | */ 45 | @GetMapping(produces = "application/json") 46 | public ResponseEntity>> getTariffs( 47 | @RequestParam(value = "date_from", required = false) LocalDateTime dateFrom, 48 | @RequestParam(value = "date_to", required = false) LocalDateTime dateTo, 49 | @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset, 50 | @RequestParam(value = "limit", required = false) Integer limit, 51 | HttpServletRequest request) { 52 | int adjustedLimit = paginationService.adjustLimitByMax(limit); 53 | 54 | List tariffs = cpoTariffService.getAll(dateFrom, dateTo, offset, adjustedLimit); 55 | long totalCount = cpoTariffService.getTotalCount(dateFrom, dateTo); 56 | 57 | ResponseFormat> responseFormat = new ResponseFormat>() 58 | .build(OcpiStatusCode.SUCCESS, tariffs); 59 | HttpHeaders responseHeaders = paginationService.buildHeader(offset, adjustedLimit, request, totalCount); 60 | 61 | return ResponseEntity.ok() 62 | .headers(responseHeaders) 63 | .body(responseFormat); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/HubClientInfoController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ClientInfoDto; 4 | import com.extrawest.ocpi.model.dto.ResponseFormat; 5 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 6 | import com.extrawest.ocpi.service.HubClientInfoService; 7 | import com.extrawest.ocpi.service.pagination.PaginationService; 8 | import io.swagger.v3.oas.annotations.tags.Tag; 9 | import jakarta.servlet.http.HttpServletRequest; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.http.HttpHeaders; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestParam; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | @RestController 22 | @RequestMapping("/hub/api/2.2.1/hubClientInfo") 23 | @Tag(name = "HubClientInfo") 24 | public class HubClientInfoController { 25 | 26 | protected final HubClientInfoService hubClientInfoService; 27 | protected final PaginationService paginationService; 28 | 29 | protected HubClientInfoController(@Autowired HubClientInfoService hubClientInfoService, 30 | @Autowired PaginationService paginationService) { 31 | this.hubClientInfoService = hubClientInfoService; 32 | this.paginationService = paginationService; 33 | } 34 | 35 | /** 36 | * Get the list of known ClientInfo objects, last updated between the {date_from} and {date_to} paginated) 37 | * 38 | * @param dateFrom Only return ClientInfo that have last_updated after or equal to this Date/Time (inclusive). 39 | * @param dateTo Only return ClientInfo that have last_updated up to this Date/Time, but not including (exclusive). 40 | * @param offset The offset of the first object returned. Default is 0. 41 | * @param limit Maximum number of objects to GET. 42 | * @return List of all (or matching) ClientInfo objects. 43 | */ 44 | @GetMapping 45 | public ResponseEntity>> getClientInfoList( 46 | @RequestParam(value = "date_from", required = false) LocalDateTime dateFrom, 47 | @RequestParam(value = "date_to", required = false) LocalDateTime dateTo, 48 | @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset, 49 | @RequestParam(value = "limit", required = false) Integer limit, 50 | HttpServletRequest request) { 51 | int adjustedLimit = paginationService.adjustLimitByMax(limit); 52 | 53 | List clientsInfo = hubClientInfoService.getClientInfoList(dateFrom, dateTo, offset, adjustedLimit); 54 | long totalCount = hubClientInfoService.getTotalCount(dateFrom, dateTo); 55 | 56 | ResponseFormat> responseFormat = new ResponseFormat>() 57 | .build(OcpiStatusCode.SUCCESS, clientsInfo); 58 | HttpHeaders responseHeaders = paginationService.buildHeader(offset, adjustedLimit, request, totalCount); 59 | 60 | return ResponseEntity.ok() 61 | .headers(responseHeaders) 62 | .body(responseFormat); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/tariff/TariffDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.tariff; 2 | 3 | import com.extrawest.ocpi.model.dto.ClientOwnedObject; 4 | import com.extrawest.ocpi.model.dto.DisplayText; 5 | import com.extrawest.ocpi.model.dto.Price; 6 | import com.extrawest.ocpi.model.dto.location.EnergyMix; 7 | import com.extrawest.ocpi.model.enums.TariffType; 8 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 9 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 10 | import com.fasterxml.jackson.annotation.JsonProperty; 11 | import jakarta.validation.constraints.NotBlank; 12 | import jakarta.validation.constraints.NotEmpty; 13 | import jakarta.validation.constraints.Size; 14 | import lombok.Data; 15 | import lombok.EqualsAndHashCode; 16 | import lombok.NoArgsConstructor; 17 | 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | @EqualsAndHashCode(callSuper = true) 22 | @Data 23 | @NoArgsConstructor 24 | public class TariffDto extends ClientOwnedObject implements OcpiRequestData, OcpiResponseData { 25 | /** 26 | * ISO-4217 code of the currency of this tariff. 27 | */ 28 | @NotBlank 29 | @Size(min = 1, max = 3) 30 | private String currency; 31 | 32 | /** 33 | * Defines the type of the tariff. This allows for distinction in case of given Charging Preferences. 34 | * When omitted, this tariff is valid for all sessions. 35 | */ 36 | private TariffType type; 37 | 38 | /** 39 | * List of multi-language alternative tariff info texts 40 | */ 41 | @JsonProperty("tariff_alt_text") 42 | private List tariffAltText; 43 | 44 | /** 45 | * List of multi-language alternative tariff info texts 46 | */ 47 | @JsonProperty("tariff_alt_url") 48 | private String tariffAltUrl; 49 | 50 | /** 51 | * When this field is set, a Charging Session with this tariff will at least cost this 52 | * amount. This is different from a FLAT fee (Start Tariff, Transaction Fee), as a 53 | * FLAT fee is a fixed amount that has to be paid for any Charging Session. A 54 | * minimum price indicates that when the cost of a Charging Session is lower than 55 | * this amount, the cost of the Session will be equal to this amount. 56 | */ 57 | @JsonProperty("min_price") 58 | private Price minPrice; 59 | 60 | /** 61 | * When this field is set, a Charging Session with this tariff will NOT cost more than 62 | * this amount. 63 | */ 64 | @JsonProperty("max_price") 65 | private Price maxPrice; 66 | 67 | /** 68 | * List of Tariff Elements. 69 | */ 70 | @NotEmpty 71 | private List elements; 72 | 73 | /** 74 | * The time when this tariff becomes active, in UTC, time_zone field of the 75 | * Location can be used to convert to local time. Typically used for a new tariff that 76 | * is already given with the location, before it becomes active. 77 | */ 78 | @JsonProperty("start_date_time") 79 | private LocalDateTime startDateTime; 80 | 81 | /** 82 | * The time after which this tariff is no longer valid, in UTC, time_zone field if the 83 | * Location can be used to convert to local time. Typically used when this tariff is 84 | * going to be replaced with a different tariff in the near future. (See note below) 85 | */ 86 | @JsonProperty("end_date_time") 87 | private LocalDateTime endDateTime; 88 | 89 | /** 90 | * Details on the energy supplied with this tariff. 91 | */ 92 | @JsonProperty("energy_mix") 93 | private EnergyMix energyMix; 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/enums/CdrDimensionType.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.enums; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import static com.extrawest.ocpi.util.EnumUtil.findByField; 7 | 8 | /** 9 | * This enumeration contains allowed values for CdrDimensions, which are used to define dimensions of ChargingPeriods 10 | * in both CDRs and Sessions. Some of these values are not useful for CDRs, and SHALL therefor only be used in Sessions, 11 | * these are marked: Session Only 12 | */ 13 | public enum CdrDimensionType { 14 | /** 15 | * Average charging current during this ChargingPeriod: defined in A (Ampere). When negative, the current 16 | * is flowing from the EV to the grid. 17 | * Session Only 18 | */ 19 | CURRENT("CURRENT"), 20 | /** 21 | * Total amount of energy (dis-)charged during this ChargingPeriod: defined in kWh. When negative, more energy 22 | * was feed into the grid then charged into the EV. Default step_size is 1. 23 | */ 24 | ENERGY("ENERGY"), 25 | /** 26 | * Total amount of energy feed back into the grid: defined in kWh. 27 | * Session Only 28 | */ 29 | ENERGY_EXPORT("ENERGY_EXPORT"), 30 | /** 31 | * Total amount of energy charged, defined in kWh. 32 | * Session Only 33 | */ 34 | ENERGY_IMPORT("ENERGY_IMPORT"), 35 | /** 36 | * Sum of the maximum current over all phases, reached during this ChargingPeriod: defined in A (Ampere). 37 | */ 38 | MAX_CURRENT("MAX_CURRENT"), 39 | /** 40 | * Sum of the minimum current over all phases, reached during this ChargingPeriod, when negative, current 41 | * has flowed from the EV to the grid. Defined in A (Ampere). 42 | */ 43 | MIN_CURRENT("MIN_CURRENT"), 44 | /** 45 | * Maximum power reached during this ChargingPeriod: defined in kW (Kilowatt). 46 | */ 47 | MAX_POWER("MAX_POWER"), 48 | /** 49 | * Minimum power reached during this ChargingPeriod: defined in kW (Kilowatt), when negative, the power 50 | * has flowed from the EV to the grid. 51 | */ 52 | MIN_POWER("MIN_POWER"), 53 | /** 54 | * Time during this ChargingPeriod not charging: defined in hours, default step_size multiplier is 1 second. 55 | */ 56 | PARKING_TIME("PARKING_TIME"), 57 | /** 58 | * Average power during this ChargingPeriod: defined in kW (Kilowatt). When negative, the power 59 | * is flowing from the EV to the grid. 60 | * Session Only 61 | */ 62 | POWER("POWER"), 63 | /** 64 | * Time during this ChargingPeriod Charge Point has been reserved and not yet been in use for this customer: 65 | * defined in hours, default step_size multiplier is 1 second. 66 | */ 67 | RESERVATION_TIME("RESERVATION_TIME"), 68 | /** 69 | * Current state of charge of the EV, in percentage, values allowed: 0 to 100. See note below. 70 | */ 71 | STATE_OF_CHARGE("STATE_OF_CHARGE"), 72 | /** 73 | * Time charging during this ChargingPeriod: defined in hours, default step_size multiplier is 1 second. 74 | * Session Only 75 | */ 76 | TIME("TIME"); 77 | private final String value; 78 | 79 | CdrDimensionType(String value) { 80 | this.value = value; 81 | } 82 | 83 | @JsonCreator 84 | public static CdrDimensionType fromValue(String value) { 85 | return findByField( 86 | CdrDimensionType.class, 87 | CdrDimensionType::value, 88 | value 89 | ); 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | return this.value; 95 | } 96 | 97 | @JsonValue 98 | public String value() { 99 | return this.value; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/Connector.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.dto.AbstractDomainObject; 4 | import com.extrawest.ocpi.model.enums.ConnectorFormat; 5 | import com.extrawest.ocpi.model.enums.ConnectorType; 6 | import com.extrawest.ocpi.model.enums.PowerType; 7 | import com.extrawest.ocpi.model.markers.LocationData; 8 | import com.fasterxml.jackson.annotation.JsonProperty; 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.NotNull; 11 | import jakarta.validation.constraints.Size; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | 15 | import java.time.LocalDateTime; 16 | import java.util.List; 17 | 18 | /** 19 | * A Connector is the socket or cable and plug available for the EV to use. A single EVSE may provide 20 | * multiple Connectors but only one of them can be in use at the same time. 21 | * A Connector always belongs to an EVSE object. 22 | */ 23 | @Data 24 | @NoArgsConstructor 25 | public class Connector extends AbstractDomainObject implements LocationData { 26 | /** 27 | * Identifier of the Connector within the EVSE. Two Connectors may have 28 | * the same id as long as they do not belong to the same EVSE object. 29 | */ 30 | @NotBlank 31 | @Size(max = 36) 32 | @JsonProperty("id") 33 | private String connectorId; 34 | /** 35 | * The standard of the installed connector. 36 | */ 37 | @NotNull 38 | @JsonProperty("standard") 39 | private ConnectorType standard; 40 | /** 41 | * The format (socket/cable) of the installed connector. 42 | */ 43 | @NotNull 44 | @JsonProperty("format") 45 | private ConnectorFormat format; 46 | @NotNull 47 | @JsonProperty("power_type") 48 | private PowerType powerType; 49 | /** 50 | * Maximum voltage of the connector (line to neutral for AC_3_PHASE), in volt [V]. 51 | * For example: DC Chargers might vary the voltage during charging when battery almost full. 52 | */ 53 | @NotNull 54 | @JsonProperty("max_voltage") 55 | private Integer maxVoltage; 56 | /** 57 | * Maximum amperage of the connector, in ampere [A]. 58 | */ 59 | @NotNull 60 | @JsonProperty("max_amperage") 61 | private Integer maxAmperage; 62 | /** 63 | * Maximum electric power that can be delivered by this connector, in Watts (W). When the maximum electric power 64 | * is lower than the calculated value from voltage and amperage, this value should be set. 65 | * For example: A DC Charge Point which can delivers up to 920V and up to 400A can be limited to a maximum 66 | * of 150kW (max_electric_power = 150000). Depending on the car, it may supply max voltage or current, 67 | * but not both at the same time. For AC Charge Points, the amount of phases used can also have influence on the 68 | * maximum power 69 | */ 70 | @JsonProperty("max_electric_power") 71 | private Integer maxElectricPower; 72 | /** 73 | * Identifiers of the currently valid charging tariffs. Multiple tariffs are possible, but only one of 74 | * each Tariff.type can be active at the same time. Tariffs with the same type are only allowed if they 75 | * are not active at the same time: start_date_time and end_date_time period not overlapping. 76 | * When preference-based smart charging is supported, one tariff for every possible ProfileType should be provided. 77 | * These tell the user about the options they have at this Connector, and what the tariff is for every option. 78 | * For a "free of charge" tariff, this field should be set and point to a defined "free of charge" tariff. 79 | */ 80 | @JsonProperty("tariff_ids") 81 | private List tariffIds; 82 | /** 83 | * URL to the operator’s terms and conditions. 84 | */ 85 | @JsonProperty("terms_and_conditions") 86 | private String termsAndConditions; 87 | /** 88 | * Timestamp when this Connector was last updated (or created). 89 | */ 90 | @NotNull 91 | @JsonProperty("last_updated") 92 | private LocalDateTime lastUpdated; 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/CpoSessionsController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ChargingPreferences; 4 | import com.extrawest.ocpi.model.dto.ResponseFormat; 5 | import com.extrawest.ocpi.model.dto.SessionDto; 6 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 7 | import com.extrawest.ocpi.service.CpoSessionsService; 8 | import com.extrawest.ocpi.service.pagination.PaginationService; 9 | import io.swagger.v3.oas.annotations.tags.Tag; 10 | import jakarta.servlet.http.HttpServletRequest; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.HttpHeaders; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.validation.Valid; 17 | import java.time.LocalDateTime; 18 | import java.util.List; 19 | 20 | @RestController 21 | @RequestMapping("/cpo/api/2.2.1/sessions") 22 | @Tag(name = "CPOSessions") 23 | public class CpoSessionsController { 24 | 25 | protected final CpoSessionsService cpoSessionsService; 26 | protected final PaginationService paginationService; 27 | 28 | protected CpoSessionsController(@Autowired CpoSessionsService cpoSessionsService, 29 | @Autowired PaginationService paginationService) { 30 | this.cpoSessionsService = cpoSessionsService; 31 | this.paginationService = paginationService; 32 | } 33 | 34 | /** 35 | * Fetch Session objects of charging sessions last updated between the {date_from} and {date_to} (paginated). 36 | * 37 | * @param dateFrom Only return Sessions that have last_updated after or equal to this Date/Time (inclusive). 38 | * @param dateTo Only return Sessions that have last_updated up to this Date/Time, but not including (exclusive). 39 | * @param offset The offset of the first object returned. Default is 0. 40 | * @param limit Maximum number of objects to GET 41 | * @return List of Session objects that match the request parameters. 42 | */ 43 | @GetMapping 44 | public ResponseEntity>> getSessions( 45 | @RequestParam(value = "date_from") LocalDateTime dateFrom, 46 | @RequestParam(value = "date_to", required = false) LocalDateTime dateTo, 47 | @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset, 48 | @RequestParam(value = "limit", required = false) Integer limit, 49 | HttpServletRequest request) { 50 | int adjustedLimit = paginationService.adjustLimitByMax(limit); 51 | 52 | List sessions = cpoSessionsService.getSessions(dateFrom, dateTo, offset, adjustedLimit); 53 | long totalCount = cpoSessionsService.getTotalCount(dateFrom, dateTo); 54 | 55 | ResponseFormat> responseFormat = new ResponseFormat>() 56 | .build(OcpiStatusCode.SUCCESS, sessions); 57 | HttpHeaders responseHeaders = paginationService.buildHeader(offset, adjustedLimit, request, totalCount); 58 | 59 | return ResponseEntity.ok() 60 | .headers(responseHeaders) 61 | .body(responseFormat); 62 | } 63 | 64 | /** 65 | * Setting Charging Preferences of an ongoing session. 66 | * 67 | * @param sessionId Setting Charging Preferences of an ongoing session. 68 | * @param chargingPreferencesDTO Updated Charging Preferences of the driver for this Session. 69 | * @return Response to the Charging Preferences PUT request. 70 | */ 71 | @PutMapping 72 | public ResponseEntity> putChargingPreferences( 73 | @RequestParam(value = "session_id") String sessionId, 74 | @RequestBody @Valid ChargingPreferences chargingPreferencesDTO) { 75 | 76 | ChargingPreferences chargingPreferences = 77 | cpoSessionsService.putChargingPreferences(sessionId, chargingPreferencesDTO); 78 | 79 | ResponseFormat responseFormat = new ResponseFormat() 80 | .build(OcpiStatusCode.SUCCESS, chargingPreferences); 81 | return ResponseEntity.ok(responseFormat); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/location/EVSE.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.location; 2 | 3 | import com.extrawest.ocpi.model.dto.AbstractDomainObject; 4 | import com.extrawest.ocpi.model.dto.DisplayText; 5 | import com.extrawest.ocpi.model.enums.Capability; 6 | import com.extrawest.ocpi.model.enums.ParkingRestriction; 7 | import com.extrawest.ocpi.model.enums.Status; 8 | import com.extrawest.ocpi.model.markers.LocationData; 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | import jakarta.validation.constraints.NotBlank; 11 | import jakarta.validation.constraints.NotEmpty; 12 | import jakarta.validation.constraints.NotNull; 13 | import jakarta.validation.constraints.Size; 14 | import lombok.AllArgsConstructor; 15 | import lombok.Data; 16 | import lombok.NoArgsConstructor; 17 | 18 | import java.time.LocalDateTime; 19 | import java.util.List; 20 | 21 | /** 22 | * The EVSE object describes the part that controls the power supply to a single EV in a single session. 23 | * It always belongs to a Location object. The object only contains directions to get from the location itself to the 24 | * EVSE (i.e. floor, physical_reference or directions). 25 | * When the directional properties of an EVSE are insufficient to reach the EVSE from the Location point, 26 | * then it typically indicates that the EVSE should be put in a different Location object 27 | * (sometimes with the same address but with different coordinates/directions). 28 | */ 29 | @Data 30 | @NoArgsConstructor 31 | @AllArgsConstructor 32 | public class EVSE extends AbstractDomainObject implements LocationData { 33 | /** 34 | * Uniquely identifies the EVSE within the CPOs platform (and sub-operator platforms). For example a 35 | * database ID or the actual "EVSE ID". This field can never be changed, modified or renamed. 36 | * This is the 'technical' identification of the EVSE, not to be used as 'human-readable' identification, 37 | * use the field evse_id for that. This field is named uid instead of id, because id could be confused 38 | * with evse_id which is an eMI3 defined field. 39 | */ 40 | @NotBlank 41 | @Size(max = 36) 42 | @JsonProperty("uid") 43 | private String uid; 44 | /** 45 | * Compliant with the following specification for EVSE ID from "eMI3 standard version V1.0" 46 | * () "Part 2: business objects." Optional because: if an evse_id is 47 | * to be re-used in the real world, the evse_id can be removed from an EVSE object if the status is set to REMOVED. 48 | */ 49 | @Size(max = 48) 50 | @JsonProperty("evse_id") 51 | private String evseId; 52 | /** 53 | * Indicates the current status of the EVSE. 54 | */ 55 | @NotNull 56 | @JsonProperty("status") 57 | private Status status; 58 | /** 59 | * Indicates a planned status update of the EVSE. 60 | */ 61 | @JsonProperty("status_schedule") 62 | private List statusSchedule; 63 | /** 64 | * List of functionalities that the EVSE is capable of. 65 | */ 66 | @JsonProperty("capabilities") 67 | private List capabilities; 68 | /** 69 | * List of available connectors on the EVSE. 70 | */ 71 | @NotEmpty 72 | @JsonProperty("connectors") 73 | private List connectors; 74 | /** 75 | * Level on which the Charge Point is located (in garage buildings) in the 76 | * locally displayed numbering scheme. 77 | */ 78 | @Size(max = 4) 79 | @JsonProperty("floor_level") 80 | private String floorLevel; 81 | /** 82 | * Coordinates of the EVSE. 83 | */ 84 | @JsonProperty("coordinates") 85 | private GeoLocation coordinates; 86 | /** 87 | * A number/string printed on the outside of the EVSE for visual 88 | * identification. 89 | */ 90 | @Size(max = 16) 91 | @JsonProperty("physical_reference") 92 | private String physicalReference; 93 | /** 94 | * Multi-language human-readable directions when more detailed information on how to reach the EVSE from 95 | * the Location is required. 96 | */ 97 | @JsonProperty("directions") 98 | private List directions; 99 | /** 100 | * The restrictions that apply to the parking spot. 101 | */ 102 | @JsonProperty("parking_restrictions") 103 | private List parkingRestrictions; 104 | /** 105 | * Links to images related to the EVSE such as photos or logos. 106 | */ 107 | @JsonProperty("images") 108 | private List images; 109 | /** 110 | * Timestamp when this EVSE or one of its Connectors was last updated (or created). 111 | */ 112 | @NotNull 113 | @JsonProperty("last_updated") 114 | private LocalDateTime lastUpdated; 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/exception/handlers/OcpiExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.exception.handlers; 2 | 3 | import com.extrawest.ocpi.exception.*; 4 | import com.extrawest.ocpi.model.dto.ResponseFormat; 5 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 6 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.http.HttpHeaders; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.HttpStatusCode; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.validation.FieldError; 13 | import org.springframework.web.bind.MethodArgumentNotValidException; 14 | import org.springframework.web.bind.annotation.ControllerAdvice; 15 | import org.springframework.web.bind.annotation.ExceptionHandler; 16 | import org.springframework.web.context.request.WebRequest; 17 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | @Slf4j 23 | @ControllerAdvice 24 | public class OcpiExceptionHandler extends ResponseEntityExceptionHandler { 25 | 26 | @ExceptionHandler(value = {OcpiInvalidParametersException.class, 27 | OcpiGeneralClientException.class, 28 | OcpiResourceNotFoundException.class, 29 | OcpiUnknownTokenException.class, 30 | NotEnoughInformationException.class}) 31 | public ResponseEntity> handleException(RuntimeException ex) { 32 | HttpStatus httpStatus; 33 | OcpiStatusCode ocpiStatusCode; 34 | 35 | if (ex instanceof OcpiInvalidParametersException) { 36 | httpStatus = HttpStatus.BAD_REQUEST; 37 | ocpiStatusCode = OcpiStatusCode.INVALID_PARAMETERS; 38 | } else if (ex instanceof OcpiGeneralClientException) { 39 | httpStatus = HttpStatus.BAD_REQUEST; 40 | ocpiStatusCode = OcpiStatusCode.CLIENT_ERROR; 41 | } else if (ex instanceof OcpiResourceNotFoundException) { 42 | httpStatus = HttpStatus.NOT_FOUND; 43 | ocpiStatusCode = OcpiStatusCode.CLIENT_ERROR; 44 | } else if (ex instanceof OcpiUnknownTokenException) { 45 | httpStatus = HttpStatus.NOT_FOUND; 46 | ocpiStatusCode = OcpiStatusCode.UNKNOWN_TOKEN; 47 | } else if (ex instanceof NotEnoughInformationException) { 48 | httpStatus = HttpStatus.BAD_REQUEST; 49 | ocpiStatusCode = OcpiStatusCode.NOT_ENOUGH_INFORMATION; 50 | } else { 51 | httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; 52 | ocpiStatusCode = OcpiStatusCode.SERVER_ERROR; 53 | } 54 | return handleExceptionInternal(ex, httpStatus, ocpiStatusCode); 55 | } 56 | 57 | 58 | private ResponseEntity> handleExceptionInternal(RuntimeException ex, 59 | HttpStatus status, 60 | OcpiStatusCode ocpiStatusCode) { 61 | 62 | ResponseFormat responseFormat = new ResponseFormat() 63 | .build(ocpiStatusCode); 64 | 65 | responseFormat.setStatusMessage(String.format("%s. %s", 66 | responseFormat.getStatusMessage(), ex.getMessage())); 67 | 68 | log.error("Request failed: {} ({})", ocpiStatusCode.getValue(), ex.getMessage()); 69 | ex.printStackTrace(); 70 | return new ResponseEntity<>(responseFormat, status); 71 | } 72 | 73 | 74 | @Override 75 | protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, 76 | HttpHeaders headers, 77 | HttpStatusCode status, 78 | WebRequest request) { 79 | 80 | ResponseFormat responseFormat = new ResponseFormat() 81 | .build(OcpiStatusCode.CLIENT_ERROR); 82 | 83 | Map errors = new HashMap<>(); 84 | ex.getBindingResult().getAllErrors().forEach((error) -> { 85 | 86 | String fieldName = ((FieldError) error).getField(); 87 | String message = error.getDefaultMessage(); 88 | errors.put(fieldName, message); 89 | }); 90 | 91 | responseFormat.setStatusMessage(String.format("%s. %s", 92 | responseFormat.getStatusMessage(), errors)); 93 | 94 | log.error("Request failed: {} ({})", OcpiStatusCode.CLIENT_ERROR.getValue(), errors); 95 | ex.printStackTrace(); 96 | return new ResponseEntity<>(responseFormat, status); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/cdr/CdrLocation.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto.cdr; 2 | 3 | import com.extrawest.ocpi.model.dto.location.GeoLocation; 4 | import com.extrawest.ocpi.model.enums.ConnectorFormat; 5 | import com.extrawest.ocpi.model.enums.ConnectorType; 6 | import com.extrawest.ocpi.model.enums.PowerType; 7 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 8 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | import jakarta.validation.constraints.NotBlank; 11 | import jakarta.validation.constraints.NotNull; 12 | import jakarta.validation.constraints.Size; 13 | import lombok.Data; 14 | import lombok.NoArgsConstructor; 15 | 16 | @Data 17 | @NoArgsConstructor 18 | public class CdrLocation implements OcpiRequestData, OcpiResponseData { 19 | /** 20 | * Uniquely identifies the location within the CPO’s platform (and sub-operator platforms). This field can 21 | * never be changed, modified or renamed. 22 | */ 23 | @NotBlank 24 | @Size(max = 36) 25 | @JsonProperty("id") 26 | private String locationId; 27 | 28 | /** 29 | * Display name of the location. 30 | */ 31 | @JsonProperty("name") 32 | @Size(max = 255) 33 | private String name; 34 | 35 | /** 36 | * Street/block name and house number if available. 37 | */ 38 | @NotBlank 39 | @Size(max = 45) 40 | @JsonProperty("address") 41 | private String address; 42 | 43 | /** 44 | * City or town. 45 | */ 46 | @NotBlank 47 | @Size(max = 45) 48 | @JsonProperty("city") 49 | private String city; 50 | 51 | /** 52 | * Postal code of the location, may only be omitted when the location has no postal code: in some countries 53 | * charging locations at highways don’t have postal codes. 54 | */ 55 | @JsonProperty("postal_code") 56 | @Size(max = 10) 57 | private String postalCode; 58 | 59 | /** 60 | * State only to be used when relevant. 61 | */ 62 | @JsonProperty("state") 63 | @Size(max = 20) 64 | private String state; 65 | 66 | /** 67 | * ISO 3166-1 alpha-3 code for the country of this location. 68 | */ 69 | @JsonProperty("country") 70 | @NotBlank 71 | @Size(max = 3) 72 | private String country; 73 | 74 | /** 75 | * Coordinates of the location. 76 | */ 77 | @JsonProperty("coordinates") 78 | @NotNull 79 | private GeoLocation coordinates; 80 | 81 | /** 82 | * Uniquely identifies the EVSE within the CPO’s platform (and suboperator platforms). 83 | * For example a database unique ID or the actual EVSE ID. This field can never be changed, modified or renamed. 84 | * This is the technical identification of the EVSE, not to be used as human-readable identification, use the field: 85 | * evse_id for that. Allowed to be set to: #NA when this CDR is created for a reservation that never resulted 86 | * in a charging session. 87 | */ 88 | @NotBlank 89 | @Size(max = 36) 90 | @JsonProperty("evse_uid") 91 | private String evseUid; 92 | 93 | /** 94 | * Compliant with the following specification for EVSE ID from "eMI3 standard version V1.0" 95 | * (...) "Part 2: business objects.". 96 | * Allowed to be set to: #NA when this CDR is created for a reservation that never resulted in a charging session. 97 | */ 98 | @NotBlank 99 | @Size(max = 48) 100 | @JsonProperty("evse_id") 101 | private String evseId; 102 | 103 | /** 104 | * Identifier of the connector within the EVSE. Allowed to be set to: #NA when this CDR is created 105 | * for a reservation that never resulted in a charging session. 106 | */ 107 | @NotBlank 108 | @Size(max = 36) 109 | @JsonProperty("connector_id") 110 | private String connectorId; 111 | 112 | /** 113 | * The standard of the installed connector. When this CDR is created for a reservation that never resulted 114 | * in a charging session, this field can be set to any value and should be ignored by the Receiver. 115 | */ 116 | @JsonProperty("connector_standard") 117 | private ConnectorType connectorStandard; 118 | 119 | /** 120 | * The format (socket/cable) of the installed connector. When this CDR is created for a reservation that 121 | * never resulted in a charging session, this field can be set to any value and should be ignored by the Receiver. 122 | */ 123 | @JsonProperty("connector_format") 124 | private ConnectorFormat connectorFormat; 125 | 126 | /** 127 | * When this CDR is created for a reservation that never resulted in a charging session, this field can be set 128 | * to any value and should be ignored by the Receiver. 129 | */ 130 | @JsonProperty("connector_power_type") 131 | private PowerType connectorPowerType; 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/controller/CpoChargingProfilesController.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.controller; 2 | 3 | import com.extrawest.ocpi.model.dto.ResponseFormat; 4 | import com.extrawest.ocpi.model.dto.charging_profile.ChargingProfileResponse; 5 | import com.extrawest.ocpi.model.dto.charging_profile.SetChargingProfile; 6 | import com.extrawest.ocpi.model.enums.status_codes.OcpiStatusCode; 7 | import com.extrawest.ocpi.service.CpoChargingProfilesService; 8 | import io.swagger.v3.oas.annotations.tags.Tag; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.validation.Valid; 14 | 15 | @RestController 16 | @RequestMapping("/cpo/api/2.2.1/chargingProfiles") 17 | @Tag(name = "CPOChargingProfiles") 18 | public class CpoChargingProfilesController { 19 | 20 | protected final CpoChargingProfilesService cpoChargingProfilesService; 21 | 22 | protected CpoChargingProfilesController(@Autowired CpoChargingProfilesService cpoChargingProfilesService) { 23 | this.cpoChargingProfilesService = cpoChargingProfilesService; 24 | } 25 | 26 | /** 27 | * Gets the ActiveChargingProfile for a specific charging session. 28 | * 29 | * @param sessionId The unique id that identifies the session in the CPO platform. 30 | * @param duration Length of the requested ActiveChargingProfile in seconds Duration in seconds. * 31 | * @param responseUrl URL that the ActiveChargingProfileResult POST should be send to. This URL might contain 32 | * an unique ID to be able to distinguish between GET ActiveChargingProfile requests. 33 | * @return Result of the ActiveChargingProfile request, by the Receiver (Typically CPO), not the location/EVSE. 34 | * So this indicates if the Receiver understood the ChargingProfile request and was able to send it to the EVSE. 35 | * This is not the response by the Charge Point. 36 | */ 37 | @GetMapping("/{session_id}/{duration}/{response_url}") 38 | public ResponseEntity> getChargingProfile( 39 | @PathVariable(value = "session_id") String sessionId, 40 | @PathVariable(value = "duration") Integer duration, 41 | @PathVariable(value = "response_url") String responseUrl 42 | ) { 43 | ChargingProfileResponse chargingProfileResponse = 44 | cpoChargingProfilesService.getChargingProfile(sessionId, duration, responseUrl); 45 | 46 | ResponseFormat responseFormat = new ResponseFormat() 47 | .build(OcpiStatusCode.SUCCESS, chargingProfileResponse); 48 | return ResponseEntity.ok(responseFormat); 49 | } 50 | 51 | /** 52 | * Creates/updates a ChargingProfile for a specific charging session. 53 | * 54 | * @param sessionId The unique id that identifies the session in the CPO platform. 55 | * @param setChargingProfileRequestDTO SetChargingProfile object with information needed to set/update 56 | * the Charging Profile for a session. 57 | * @return Result of the ChargingProfile PUT request, by the CPO (not the location/EVSE). So this indicates if 58 | * the CPO understood the ChargingProfile PUT request and was able to send it to the EVSE. 59 | * This is not the response by the Charge Point. 60 | */ 61 | @PutMapping("/{session_id}") 62 | public ResponseEntity putChargingProfile( 63 | @PathVariable(value = "session_id") String sessionId, 64 | @RequestBody @Valid SetChargingProfile setChargingProfileRequestDTO) { 65 | return ResponseEntity.ok(cpoChargingProfilesService.putChargingProfile(sessionId, setChargingProfileRequestDTO)); 66 | } 67 | 68 | /** 69 | * Cancels an existing ChargingProfile for a specific charging session. 70 | * 71 | * @param sessionId The unique id that identifies the session in the CPO platform. 72 | * @param responseUrl URL that the ClearProfileResult POST should be sent to. This URL might contain 73 | * unique ID to be able to distinguish between DELETE ChargingProfile requests. 74 | * @return Result of the ChargingProfile DELETE request, by the CPO (not the location/EVSE). So this indicates 75 | * if the CPO understood the ChargingProfile DELETE request and was able to send it to the EVSE. This is 76 | * not the response by the Charge Point. 77 | */ 78 | @DeleteMapping("/{session_id}/{response_url}") 79 | public ResponseEntity> deleteChargingProfile( 80 | @PathVariable(value = "session_id") String sessionId, 81 | @PathVariable(value = "response_url") String responseUrl) { 82 | ChargingProfileResponse chargingProfileResponse = 83 | cpoChargingProfilesService.deleteChargingProfile(sessionId, responseUrl); 84 | 85 | ResponseFormat responseFormat = new ResponseFormat() 86 | .build(OcpiStatusCode.SUCCESS, chargingProfileResponse); 87 | return ResponseEntity.ok(responseFormat); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/extrawest/ocpi/model/dto/SessionDto.java: -------------------------------------------------------------------------------- 1 | package com.extrawest.ocpi.model.dto; 2 | 3 | import com.extrawest.ocpi.model.dto.cdr.CdrToken; 4 | import com.extrawest.ocpi.model.enums.AuthMethod; 5 | import com.extrawest.ocpi.model.enums.SessionStatus; 6 | import com.extrawest.ocpi.model.markers.OcpiRequestData; 7 | import com.extrawest.ocpi.model.markers.OcpiResponseData; 8 | import com.extrawest.ocpi.util.Constants; 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | import jakarta.validation.constraints.*; 11 | import lombok.AllArgsConstructor; 12 | import lombok.Data; 13 | import lombok.EqualsAndHashCode; 14 | import lombok.NoArgsConstructor; 15 | 16 | import java.time.LocalDateTime; 17 | import java.util.List; 18 | 19 | @EqualsAndHashCode(callSuper = true) 20 | @Data 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | public class SessionDto extends ClientOwnedObject implements OcpiRequestData, OcpiResponseData { 24 | /** 25 | * The timestamp when the session became ACTIVE in the Charge Point. 26 | * When the session is still PENDING, this field SHALL be set to the time the Session was created at the 27 | * Charge Point. When a Session goes from PENDING to ACTIVE, this field SHALL be updated to the moment 28 | * the Session went to ACTIVE in the Charge Point. 29 | */ 30 | @NotNull 31 | @JsonProperty("start_date_time") 32 | private LocalDateTime startDateTime; 33 | 34 | /** 35 | * The timestamp when the session was completed/finished, charging might have finished before the session ends, 36 | * for example: EV is full, but parking cost also has to be paid. 37 | */ 38 | @JsonProperty("end_date_time") 39 | private LocalDateTime endDateTime; 40 | 41 | /** 42 | * How many kWh were charged. 43 | */ 44 | @NotNull 45 | @Digits(integer = Integer.MAX_VALUE, fraction = 4) 46 | @JsonProperty("kwh") 47 | private Float kwh; 48 | 49 | /** 50 | * Token used to start this charging session, including all the relevant information to identify the unique token. 51 | */ 52 | @NotNull 53 | @JsonProperty("cdr_token") 54 | private CdrToken cdrToken; 55 | 56 | /** 57 | * Method used for authentication. This might change during a session, for example when the session was started 58 | * with a reservation: ReserveNow: COMMAND. When the driver arrives and starts charging using a Token that 59 | * is whitelisted: WHITELIST. 60 | */ 61 | @NotNull 62 | @JsonProperty("auth_method") 63 | private AuthMethod authMethod; 64 | 65 | /** 66 | * Reference to the authorization given by the eMSP. When the eMSP provided an authorization_reference in either: 67 | * real-time authorization, StartSession or ReserveNow this field SHALL contain the same value. When different 68 | * authorization_reference values have been given by the eMSP that are relevant to this Session, the last given 69 | * value SHALL be used here. 70 | */ 71 | @Size(min = 1, max = 36) 72 | @Pattern(regexp = Constants.ASCII_REGEXP) 73 | @JsonProperty("authorization_reference") 74 | private String authorizationReference; 75 | 76 | /** 77 | * Location.id of the Location object of this CPO, on which the charging session is/was happening. 78 | */ 79 | @NotBlank 80 | @Size(min = 1, max = 36) 81 | @Pattern(regexp = Constants.ASCII_REGEXP) 82 | @JsonProperty("location_id") 83 | private String locationId; 84 | 85 | /** 86 | * EVSE.uid of the EVSE of this Location on which the charging session is/was happening. Allowed to be set to: 87 | * #NA when this session is created for a reservation, but no EVSE yet assigned to the driver. 88 | */ 89 | @NotBlank 90 | @Size(min = 1, max = 36) 91 | @Pattern(regexp = Constants.ASCII_REGEXP) 92 | @JsonProperty("evse_uid") 93 | private String evseUid; 94 | 95 | /** 96 | * Connector.id of the Connector of this Location where the charging session is/was happening. Allowed to be set to: 97 | * #NA when this session is created for a reservation, but no connector yet assigned to the driver. 98 | */ 99 | @NotBlank 100 | @Size(min = 1, max = 36) 101 | @Pattern(regexp = Constants.ASCII_REGEXP) 102 | @JsonProperty("connector_id") 103 | private String connectorId; 104 | 105 | /** 106 | * Optional identification of the kWh meter. 107 | */ 108 | @Size(min = 1, max = 255) 109 | @JsonProperty("meter_id") 110 | private String meterId; 111 | 112 | /** 113 | * ISO 4217 code of the currency used for this session. 114 | */ 115 | @NotBlank 116 | @Size(min = 1, max = 3) 117 | @JsonProperty("currency") 118 | private String currency; 119 | 120 | /** 121 | * An optional list of Charging Periods that can be used to calculate and verify the total cost. 122 | */ 123 | @JsonProperty("charging_periods") 124 | private List chargingPeriods; 125 | 126 | /** 127 | * The total cost of the session in the specified currency. This is the price that the eMSP will have to pay to the 128 | * CPO. A total_cost of 0.00 means free of charge. When omitted, i.e. no price information is given in the Session 129 | * object, it does not imply the session is/was free of charge. 130 | */ 131 | @JsonProperty("total_cost") 132 | private Price totalCost; 133 | 134 | /** 135 | * The status of the session. 136 | */ 137 | @NotNull 138 | @JsonProperty("status") 139 | private SessionStatus status; 140 | } 141 | --------------------------------------------------------------------------------