diff --git a/citrineos/acme_account_key.pem b/citrineos/acme_account_key.pem new file mode 100644 index 00000000..e8a259c1 --- /dev/null +++ b/citrineos/acme_account_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA8ul5QIk9qf+grKDCN1sKPPYsxT4HegfPOm4APr3pISo3rJNy +Ns8MfEryn58h8QJ8w7vSh0ZPlLpJq8oY8Y9ckaiJkNIWvRJYfFjwbMeWTLKteCOQ +lYTUzKEwxhRmQB1HlKPZYo1SFjgqF7ww4q98YFQQw67gzgs/rpLeEZ4pWzOVn4qM ++cBw2B0EgLFER6MI6dVja3t2NkNYRo6Jx0zs+UPKoYci7c9WETFcGOQwv2GjpOrm +ekkCLnp7/3/5tmkKmUFbkTZU8+Qoq+Hgj8pAqgRYik2dl9WyE21PHygL7a6psKZR +kjvOCPUqCkwjVdf/7oOfp8JAWSnWtyU4sKVU+wIDAQABAoIBAANTmjL9jighVZB3 +pSE/8Gx0TJmo505PBBH/RqaVUDeBjgChhktk231qQ1dXRQ45Y/8EN/ZdSqK1SGP/ +YQcR2Qkvny6qCeCt+yM8zpIWy6KiQcjm58h8aLOis3nK9rmDDSNmeQgl+k1OmJj5 +nUvFbnUdQZuEbhS0R7t6zGq+WT+j9uCIt0DV3m2+qWw0uN8ObZpr16GOWBp8yo6+ +3LBOwQ5+9/nqwTdOLMbpsZlJd/KOQikV9izOQkmL6tC2dxPXRqKPNST5987kYY37 +H+0iIfIyvFCx5gTKpoUy2JxGvXZOlKdibaav9b4P5P663YIb4sTUVPHi1cyZPYkc +pnoLSjkCgYEA+zAuQ5i5FTLry7f5iPjGNSdWSJ8Fb8RJ6dqTBP/PDezI6K48vJJ1 +gkll3JMyXfMBScd90qq1QVAUmb8Nz2mQXCIHsNBexjeuGNBnXxB21XSM3bA+1mL9 +zJ5eUnBUgih60CJE/8jdo9GNUY0Ce0XEOTpl1LKYPJc2uSXC1y6kLcMCgYEA95C0 +6c9hat9/VDRab+0VZrMb+FBj3f1XsHGRSWbQDuWuO7CfOEYhwTcLrE9WexTEjNL6 +6IVvN3CqhcxGTHD4fgW+9QFGAXC2SoNKCsPokzC1X1ikUusPtriyn/IvUP6qYHus +eB4xsCD5ulyGfJJRbBRa5dmzlqxnKyimPfC+MGkCgYEAxoibWHQifX3k3vyHb1pp +luODkByYOHGllf9bSo1BwxjO5xGoEceUtyh6KS/ylE0YTI8vhM3GO1wnHCnkqXYf +UqLW/0qCThr+MMCvo3So6CeZmzLNR7ewMAVQOcptEP8bqtwbOyww+mULVFSmjHZl +FHJyv/101BcUepw89sT3oO8CgYB4GQI63vkCcLQDdHZfD+Ou87rg5pbcDVfp5940 +fqT2ZSP2HwPOt+8OHZcTG1X31aZYLs272WePvJ9s0yFTWgailEUD9H8ymaxFT5Wu +zUVZimqie40UEKaJ3OYCw+mCYFjk/3o2t2cha43ag6JWcmD/joxeLxN5R9+wx0KG +j/Cj6QKBgF0WuXRkQwItF2F/swibuFcnqlGjz6I5k5wBuuZiRk8PcPgCPSyoWald +YzN6CLKfQSIZjO0fj7S329hc+CbKx1o+HNPjC72lP9fC/KCh8FAxMhUkcCwP7Bvt +fpRG66KTIz6EZtWWAI/VjWxUo9LOX4YVTBSVLeMI+bNmKHw4VLKI +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/citrineos/add-charger-and-rfid-card.sh b/citrineos/add-charger-and-rfid-card.sh new file mode 100755 index 00000000..e7d474e6 --- /dev/null +++ b/citrineos/add-charger-and-rfid-card.sh @@ -0,0 +1,215 @@ +#!/usr/bin/env bash + +# Configuration +DIRECTUS_API_URL="http://localhost:8055" +CHARGEPOINT_ID="cp001" +CP_PASSWORD="DEADBEEFDEADBEEF" +DIRECTUS_EMAIL="admin@citrineos.com" +DIRECTUS_PASSWORD="CitrineOS!" +ID_TOKEN="DEADBEEF" +ID_TOKEN_TYPE="ISO14443" + +#TODO put github images +PROJECT_LOGO_IMAGE_URL="https://public-citrineos-logo.s3.amazonaws.com/Citrine-Directus-Project-Logo.png" +PUBLIC_BACKGROUND_IMAGE_URL="https://public-citrineos-logo.s3.amazonaws.com/Citrine-Directus-Public-Background.png" + +# Function to get the Directus token +get_directus_token() { + local login_url="${DIRECTUS_API_URL}/auth/login" + local json_body=$(printf '{"email": "%s", "password": "%s"}' "$DIRECTUS_EMAIL" "$DIRECTUS_PASSWORD") + local response=$(curl -s -X POST "$login_url" -H "Content-Type: application/json" -d "$json_body") + + # Extract token from the response + local token=$(jq -r '.data.access_token' <<< "$response") + echo "$token" +} + +# Function to upload an image via URL to Directus +upload_image() { + + local token=$1 + local image_url=$2 + local title=$3 + local response=$(curl -s -X POST "${DIRECTUS_API_URL}/files/import" \ + -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/json" \ + -d "{ + \"url\": \"${image_url}\", + \"data\": { + + \"title\": \"${title}\" + } + }"| tee /dev/tty && echo) + + local file_id=$(jq -r '.data.id' <<< "$response") + + echo "$file_id" +} + +# Function to set the project image +set_project_image() { + local token=$1 + local project_logo=$2 + local project_background=$3 + curl -s -X PATCH "${DIRECTUS_API_URL}/settings" \ + -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/json" \ + -d "{ + \"project_logo\": \"${project_logo}\", + \"public_background\": \"${project_background}\" + }" | tee /dev/tty && echo +} + +# Function to add a new location +add_location() { + local token=$1 + local response=$(curl -s -X POST "${DIRECTUS_API_URL}/items/Locations" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${token}" \ + -d '{ + "id": "2", + "name": "New EVerst", + "coordinates": { + "type": "Point", + "coordinates": [-74.0620872, 41.041548] + } + }' | tee /dev/tty && echo) + + local location_id=$(jq -r '.data.id' <<< "$response") + echo "$location_id" +} + +# Function to add a charging station +add_charging_station() { + local token="$1" + local location_id="$2" + local chargepointId="$3" + curl -s --request POST \ + --url "${DIRECTUS_API_URL}/items/ChargingStations" \ + --header "Authorization: Bearer $token" \ + --header "Content-Type: application/json" \ + --data '{ + "id": "'"$chargepointId"'", + "locationId": "'"$location_id"'" + }' | tee /dev/tty && echo +} + +# Function to update SP1 password +add_cp001_password() { + local response + local success=false + local attempt=1 + local passwordString=$1 + + until $success; do + echo "Attempt $attempt: Updating SP1 password..." + response=$(curl -s -o /dev/null -w "%{http_code}" --location --request PUT "http://localhost:8080/data/monitoring/variableAttribute?stationId=${CHARGEPOINT_ID}&setOnCharger=true" \ + --header "Content-Type: application/json" \ + --data-raw '{ + "component": { + "name": "SecurityCtrlr" + }, + "variable": { + "name": "BasicAuthPassword" + }, + "variableAttribute": [ + { + "value": "'"$passwordString"'" + } + ], + "variableCharacteristics": { + "dataType": "passwordString", + "supportsMonitoring": false + } + }' | tee /dev/tty) + + + if [[ $response -ge 200 && $response -lt 300 ]]; then + echo "Password update successful." + success=true + else + echo "Password update failed with HTTP status: $response. Retrying in 2 second..." + sleep 2 + ((attempt++)) + fi + done +} + +# Function to add new ev driver authorization information +add_evdriver_authorization() { + local response + local success=false + local attempt=1 + local idToken=$1 + local idTokenType=$2 + + until $success; do + echo "Attempt $attempt: Adding ev driver authorization..." + response=$(curl -s -o /dev/null -w "%{http_code}" --location --request PUT "http://localhost:8080/data/evdriver/authorization?idToken=${idToken}&type=${idTokenType}" \ + --header "Content-Type: application/json" \ + --data-raw '{ + "idToken": { + "idToken": "'"$idToken"'", + "type": "'"$idTokenType"'" + }, + "idTokenInfo": { + "status": "Accepted" + } + }' | tee /dev/tty) + + + if [[ $response -ge 200 && $response -lt 300 ]]; then + echo "Authorization update successful." + success=true + else + echo "Authorization update failed with HTTP status: $response. Retrying in 2 second..." + sleep 2 + ((attempt++)) + fi + done +} + +# Main script execution +TOKEN=$(get_directus_token) +echo "Received Token: $TOKEN" + +if [ -z "$TOKEN" ]; then + echo "Failed to retrieve access token." + exit 1 +fi + +# Upload image and set as project logo +echo "Uploading project images..." +FILE_ID_LOGO=$(upload_image "$TOKEN" "$PROJECT_LOGO_IMAGE_URL" "Citrine Logo") + +if [ -z "$FILE_ID_LOGO" ]; then + echo "Failed to upload project image." + exit 1 +fi +FILE_ID_BACKGROUND=$(upload_image "$TOKEN" "$PUBLIC_BACKGROUND_IMAGE_URL" "Citrine Background") +if [ -z "$FILE_ID_BACKGROUND" ]; then + echo "Failed to upload project image." + exit 1 +fi + +echo "Setting project image..." +set_project_image "$TOKEN" "$FILE_ID_LOGO" "$FILE_ID_BACKGROUND" + +echo "Adding a new location..." +LOCATION_ID=$(add_location "$TOKEN") + +if [ -z "$LOCATION_ID" ]; then + echo "Failed to add new location." + exit 1 +fi + +echo "Location ID: $LOCATION_ID" + +echo "Adding new station..." +add_charging_station "$TOKEN" "$LOCATION_ID" "$CHARGEPOINT_ID" + +echo "Add cp001 password to citrine..." +add_cp001_password "$CP_PASSWORD" + +echo "Add ev driver rfid authorization to citrine..." +add_evdriver_authorization "$ID_TOKEN" "$ID_TOKEN_TYPE" \ No newline at end of file diff --git a/citrineos/add-charger.sh b/citrineos/add-charger.sh new file mode 100755 index 00000000..4671aeab --- /dev/null +++ b/citrineos/add-charger.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash + +# Configuration +DIRECTUS_API_URL="http://localhost:8055" +CHARGEPOINT_ID="cp001" +CP_PASSWORD="DEADBEEFDEADBEEF" +DIRECTUS_EMAIL="admin@citrineos.com" +DIRECTUS_PASSWORD="CitrineOS!" + +#TODO put github images +PROJECT_LOGO_IMAGE_URL="https://public-citrineos-logo.s3.amazonaws.com/Citrine-Directus-Project-Logo.png" +PUBLIC_BACKGROUND_IMAGE_URL="https://public-citrineos-logo.s3.amazonaws.com/Citrine-Directus-Public-Background.png" + +# Function to get the Directus token +get_directus_token() { + local login_url="${DIRECTUS_API_URL}/auth/login" + local json_body=$(printf '{"email": "%s", "password": "%s"}' "$DIRECTUS_EMAIL" "$DIRECTUS_PASSWORD") + local response=$(curl -s -X POST "$login_url" -H "Content-Type: application/json" -d "$json_body") + + # Extract token from the response + local token=$(jq -r '.data.access_token' <<< "$response") + echo "$token" +} + +# Function to upload an image via URL to Directus +upload_image() { + + local token=$1 + local image_url=$2 + local title=$3 + local response=$(curl -s -X POST "${DIRECTUS_API_URL}/files/import" \ + -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/json" \ + -d "{ + \"url\": \"${image_url}\", + \"data\": { + + \"title\": \"${title}\" + } + }"| tee /dev/tty && echo) + + local file_id=$(jq -r '.data.id' <<< "$response") + + echo "$file_id" +} + +# Function to set the project image +set_project_image() { + local token=$1 + local project_logo=$2 + local project_background=$3 + curl -s -X PATCH "${DIRECTUS_API_URL}/settings" \ + -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/json" \ + -d "{ + \"project_logo\": \"${project_logo}\", + \"public_background\": \"${project_background}\" + }" | tee /dev/tty && echo +} + +# Function to add a new location +add_location() { + local token=$1 + local response=$(curl -s -X POST "${DIRECTUS_API_URL}/items/Locations" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${token}" \ + -d '{ + "id": "2", + "name": "New EVerst", + "coordinates": { + "type": "Point", + "coordinates": [-74.0620872, 41.041548] + } + }' | tee /dev/tty && echo) + + local location_id=$(jq -r '.data.id' <<< "$response") + echo "$location_id" +} + +# Function to add a charging station +add_charging_station() { + local token="$1" + local location_id="$2" + local chargepointId="$3" + curl -s --request POST \ + --url "${DIRECTUS_API_URL}/items/ChargingStations" \ + --header "Authorization: Bearer $token" \ + --header "Content-Type: application/json" \ + --data '{ + "id": "'"$chargepointId"'", + "locationId": "'"$location_id"'" + }' | tee /dev/tty && echo +} + +# Function to update SP1 password +add_cp001_password() { + local response + local success=false + local attempt=1 + local passwordString=$1 + + until $success; do + echo "Attempt $attempt: Updating SP1 password..." + response=$(curl -s -o /dev/null -w "%{http_code}" --location --request PUT "http://localhost:8080/data/monitoring/variableAttribute?stationId=${CHARGEPOINT_ID}&setOnCharger=true" \ + --header "Content-Type: application/json" \ + --data-raw '{ + "component": { + "name": "SecurityCtrlr" + }, + "variable": { + "name": "BasicAuthPassword" + }, + "variableAttribute": [ + { + "value": "'"$passwordString"'" + } + ], + "variableCharacteristics": { + "dataType": "passwordString", + "supportsMonitoring": false + } + }' | tee /dev/tty) + + + if [[ $response -ge 200 && $response -lt 300 ]]; then + echo "Password update successful." + success=true + else + echo "Password update failed with HTTP status: $response. Retrying in 2 second..." + sleep 2 + ((attempt++)) + fi + done +} + +# Main script execution +TOKEN=$(get_directus_token) +echo "Received Token: $TOKEN" + +if [ -z "$TOKEN" ]; then + echo "Failed to retrieve access token." + exit 1 +fi + +# Upload image and set as project logo +echo "Uploading project images..." +FILE_ID_LOGO=$(upload_image "$TOKEN" "$PROJECT_LOGO_IMAGE_URL" "Citrine Logo") + +if [ -z "$FILE_ID_LOGO" ]; then + echo "Failed to upload project image." + exit 1 +fi +FILE_ID_BACKGROUND=$(upload_image "$TOKEN" "$PUBLIC_BACKGROUND_IMAGE_URL" "Citrine Background") +if [ -z "$FILE_ID_BACKGROUND" ]; then + echo "Failed to upload project image." + exit 1 +fi + +echo "Setting project image..." +set_project_image "$TOKEN" "$FILE_ID_LOGO" "$FILE_ID_BACKGROUND" + +echo "Adding a new location..." +LOCATION_ID=$(add_location "$TOKEN") + +if [ -z "$LOCATION_ID" ]; then + echo "Failed to add new location." + exit 1 +fi + +echo "Location ID: $LOCATION_ID" + +echo "Adding new station..." +add_charging_station "$TOKEN" "$LOCATION_ID" "$CHARGEPOINT_ID" + +echo "Add cp001 password to citrine..." +add_cp001_password "$CP_PASSWORD" \ No newline at end of file diff --git a/demo-iso15118-2-ac-plus-ocpp.sh b/demo-iso15118-2-ac-plus-ocpp.sh index 6e054ddd..684963da 100755 --- a/demo-iso15118-2-ac-plus-ocpp.sh +++ b/demo-iso15118-2-ac-plus-ocpp.sh @@ -4,14 +4,16 @@ DEMO_REPO="https://github.com/everest/everest-demo.git" DEMO_BRANCH="main" -CSMS_REPO="https://github.com/louisg1337/maeve-csms.git" -# CSMS_BRANCH="b990d0eddf2bf80be8d9524a7b08029fbb305c7d" # patch files are based on this commit -CSMS_BRANCH="set_charging_profile" -CSMS="maeve" +MAEVE_REPO="https://github.com/louisg1337/maeve-csms.git" +# MAEVE_BRANCH="b990d0eddf2bf80be8d9524a7b08029fbb305c7d" # patch files are based on this commit +MAEVE_BRANCH="set_charging_profile" +CITRINEOS_REPO="https://github.com/citrineos/citrineos-core.git" +CITRINEOS_BRANCH="feature/everest-demo" -usage="usage: $(basename "$0") [-r ] [-b ] [-c ] [-j|1|2|3] [-h] + +usage="usage: $(basename "$0") [-r ] [-b ] [-j|1|2|3|c] [-h] This script will run EVerest ISO 15118-2 AC charging with OCPP demos. @@ -21,26 +23,24 @@ directory to the -r option (e.g., '-r \$(pwd)'). where: -r URL to everest-demo repo to use (default: $DEMO_REPO) -b Branch of everest-demo repo to use (default: $DEMO_BRANCH) - -c Use CitrineOS CSMS (default: MaEVe) -j OCPP v1.6j -1 OCPP v2.0.1 Security Profile 1 -2 OCPP v2.0.1 Security Profile 2 -3 OCPP v2.0.1 Security Profile 3 + -c Use CitrineOS CSMS (default: MaEVe) -h Show this message" DEMO_VERSION= DEMO_COMPOSE_FILE_NAME= +DEMO_CSMS=maeve # loop through positional options/arguments -while getopts ':r:b:cj123h' option; do +while getopts ':r:b:j123ch' option; do case "$option" in r) DEMO_REPO="$OPTARG" ;; b) DEMO_BRANCH="$OPTARG" ;; - c) CSMS="citrine" - CSMS_REPO="https://github.com/citrineos/citrineos-core" - CSMS_BRANCH="63670f3adc09266a0977862d972b0f7e440c577f" ;; j) DEMO_VERSION="v1.6j" DEMO_COMPOSE_FILE_NAME="docker-compose.ocpp16j.yml" ;; 1) DEMO_VERSION="v2.0.1-sp1" @@ -49,6 +49,7 @@ while getopts ':r:b:cj123h' option; do DEMO_COMPOSE_FILE_NAME="docker-compose.ocpp201.yml" ;; 3) DEMO_VERSION="v2.0.1-sp3" DEMO_COMPOSE_FILE_NAME="docker-compose.ocpp201.yml" ;; + c) DEMO_CSMS="citrineos" ;; h) echo -e "$usage"; exit ;; \?) echo -e "illegal option: -$OPTARG\n" >&2 echo -e "$usage" >&2 @@ -65,7 +66,6 @@ if [[ ! "${DEMO_VERSION}" ]]; then exit 1 fi - DEMO_DIR="$(mktemp -d)" @@ -84,6 +84,7 @@ echo "DEMO BRANCH: $DEMO_BRANCH" echo "DEMO VERSION: $DEMO_VERSION" echo "DEMO CONFIG: $DEMO_COMPOSE_FILE_NAME" echo "DEMO DIR: $DEMO_DIR" +echo "DEMO CSMS: $DEMO_CSMS" cd "${DEMO_DIR}" || exit 1 @@ -91,271 +92,145 @@ cd "${DEMO_DIR}" || exit 1 echo "Cloning EVerest from ${DEMO_REPO} into ${DEMO_DIR}/everest-demo" git clone --branch "${DEMO_BRANCH}" "${DEMO_REPO}" everest-demo -# cp -r "${DEMO_REPO}" everest-demo -if [[ "$DEMO_VERSION" != v1.6j ]]; then - echo "Cloning ${CSMS} CSMS from ${CSMS_REPO} into ${DEMO_DIR}/${CSMS}-csms and starting it" +if [[ "$DEMO_VERSION" != v1.6j && "$DEMO_CSMS" == maeve ]]; then + echo "Cloning ${DEMO_CSMS} CSMS from ${MAEVE_REPO} into ${DEMO_DIR}/${DEMO_CSMS}-csms and starting it" + git clone --branch "${MAEVE_BRANCH}" "${MAEVE_REPO}" ${DEMO_CSMS}-csms - if [[ ${CSMS} == "maeve" ]]; then - git clone --branch "${CSMS_BRANCH}" "${CSMS_REPO}" ${CSMS}-csms - else - git clone ${CSMS_REPO} ${CSMS}-csms - fi + pushd maeve-csms || exit 1 - pushd ${CSMS}-csms || exit 1 + cp ../everest-demo/manager/cached_certs_correct_name_emaid.tar.gz . - git reset --hard ${CSMS_BRANCH} - - # Set up CSMS - echo "Setting up ${CSMS}" - if [[ "$CSMS" == "citrine" ]]; then - npm run install-all - if [[ "$?" != 0 ]]; then - echo 'Error: Failed to install dependencies.' - exit 1 - fi - npm run build - if [[ "$?" != 0 ]]; then - echo 'Error: Failed to build the project.' - exit 1 - fi - else - cp ../everest-demo/manager/cached_certs_correct_name_emaid.tar.gz . - fi + echo "Patching the CSMS to disable load balancer" + patch -p1 -i ../everest-demo/maeve/maeve-csms-no-lb.patch # Set up certificates for SP2 and SP3 if [[ "$DEMO_VERSION" =~ sp2 || "$DEMO_VERSION" =~ sp3 ]]; then - if [[ "$CSMS" == "citrine" ]]; then - echo "Security profile 2/3 is not supported with Citrine yet!" - exit 1 - else - echo "Copying certs into ${DEMO_DIR}/maeve-csms/config/certificates" - tar xf cached_certs_correct_name_emaid.tar.gz - cat dist/etc/everest/certs/client/csms/CSMS_LEAF.pem \ - dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.pem \ - dist/etc/everest/certs/ca/csms/CPO_SUB_CA1.pem \ - > config/certificates/csms.pem - cat dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.pem \ - dist/etc/everest/certs/ca/csms/CPO_SUB_CA1.pem \ - > config/certificates/trust.pem - cp dist/etc/everest/certs/client/csms/CSMS_LEAF.key config/certificates/csms.key - cp dist/etc/everest/certs/ca/v2g/V2G_ROOT_CA.pem config/certificates/root-V2G-cert.pem - cp dist/etc/everest/certs/ca/mo/MO_ROOT_CA.pem config/certificates/root-MO-cert.pem - - echo "Validating that the certificates are set up correctly" - openssl verify -show_chain \ - -CAfile config/certificates/root-V2G-cert.pem \ - -untrusted config/certificates/trust.pem \ - config/certificates/csms.pem - - echo "Patching the CSMS to enable EVerest organization" - patch -p1 -i ../everest-demo/maeve/maeve-csms-everest-org.patch - - echo "Patching the CSMS to enable local mo root" - patch -p1 -i ../everest-demo/maeve/maeve-csms-local-mo-root.patch - - echo "Patching the CSMS to enable local mo root" - patch -p1 -i ../everest-demo/maeve/maeve-csms-ignore-ocsp.patch - - fi - elif [[ ${CSMS} == "maeve" ]]; then + echo "Copying certs into ${DEMO_DIR}/maeve-csms/config/certificates" + tar xf cached_certs_correct_name_emaid.tar.gz + cat dist/etc/everest/certs/client/csms/CSMS_LEAF.pem \ + dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.pem \ + dist/etc/everest/certs/ca/csms/CPO_SUB_CA1.pem \ + > config/certificates/csms.pem + cat dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.pem \ + dist/etc/everest/certs/ca/csms/CPO_SUB_CA1.pem \ + > config/certificates/trust.pem + cp dist/etc/everest/certs/client/csms/CSMS_LEAF.key config/certificates/csms.key + cp dist/etc/everest/certs/ca/v2g/V2G_ROOT_CA.pem config/certificates/root-V2G-cert.pem + cp dist/etc/everest/certs/ca/mo/MO_ROOT_CA.pem config/certificates/root-MO-cert.pem + + echo "Validating that the certificates are set up correctly" + openssl verify -show_chain \ + -CAfile config/certificates/root-V2G-cert.pem \ + -untrusted config/certificates/trust.pem \ + config/certificates/csms.pem + + echo "Patching the CSMS to enable EVerest organization" + patch -p1 -i ../everest-demo/maeve/maeve-csms-everest-org.patch + + echo "Patching the CSMS to enable local mo root" + patch -p1 -i ../everest-demo/maeve/maeve-csms-local-mo-root.patch + + echo "Patching the CSMS to enable local mo root" + patch -p1 -i ../everest-demo/maeve/maeve-csms-ignore-ocsp.patch + + else echo "Patching the CSMS to disable WSS" patch -p1 -i ../everest-demo/maeve/maeve-csms-no-wss.patch fi - # Start the CSMS - echo "Starting the CSMS" - if [[ ${CSMS} == "citrine" ]]; then - cd "Server" - # Remap the CitrineOS 8081 port (HTTP w/ no auth) to 80 port - CITRINE_DOCKER="docker-compose.yml" - - if [[ -f "$CITRINE_DOCKER" ]]; then - # Use sed to find and replace the string - sed -i '' 's/8082:8082/80:8082/g' "$CITRINE_DOCKER" - echo "Replaced mapping CitrineOS 8082 to 80 completed successfully." - else - echo "Error: File $CITRINE_DOCKER does not exist." - exit 1 - fi - fi - - docker compose build + docker compose build docker compose up -d - echo "Waiting 5s for CSMS to start..." + echo "Waiting 5s for MaEVe CSMS to start..." sleep 5 + if [[ "$DEMO_VERSION" =~ sp1 ]]; then + echo "MaEVe CSMS started, adding charge station with Security Profile 1 (note: profiles in MaEVe start with 0 so SP-0 == OCPP SP-1)" + curl http://localhost:9410/api/v0/cs/cp001 -H 'content-type: application/json' \ + -d '{"securityProfile": 0, "base64SHA256Password": "3oGi4B5I+Y9iEkYtL7xvuUxrvGOXM/X2LQrsCwf/knA="}' + elif [[ "$DEMO_VERSION" =~ sp2 ]]; then + echo "MaEVe CSMS started, adding charge station with Security Profile 2 (note: profiles in MaEVe start with 0 so SP-1 == OCPP SP-2)" + curl http://localhost:9410/api/v0/cs/cp001 -H 'content-type: application/json' \ + -d '{"securityProfile": 1, "base64SHA256Password": "3oGi4B5I+Y9iEkYtL7xvuUxrvGOXM/X2LQrsCwf/knA="}' + elif [[ "$DEMO_VERSION" =~ sp3 ]]; then + echo "MaEVe CSMS started, adding charge station with Security Profile 3 (note: profiles in MaEVe start with 0 so SP-2 == OCPP SP-3)" + curl http://localhost:9410/api/v0/cs/cp001 -H 'content-type: application/json' -d '{"securityProfile": 2}' + fi + echo "Charge station added, adding user token" - if [[ ${CSMS} == "citrine" ]]; then - # Configuration - DIRECTUS_API_URL="http://localhost:8055" - CHARGEPOINT_ID="cp001" - CP_PASSWORD="DEADBEEFDEADBEEF" - DIRECTUS_EMAIL="admin@citrineos.com" - DIRECTUS_PASSWORD="CitrineOS!" - - # Function to get the Directus token - get_directus_token() { - local login_url="${DIRECTUS_API_URL}/auth/login" - local json_body=$(printf '{"email": "%s", "password": "%s"}' "$DIRECTUS_EMAIL" "$DIRECTUS_PASSWORD") - local response=$(curl -s -X POST "$login_url" -H "Content-Type: application/json" -d "$json_body") - - # Extract token from the response - local token=$(jq -r '.data.access_token' <<< "$response") - echo "$token" - } - - # Create new charger location - add_location() { - local token=$1 - local response=$(curl -s -X POST "${DIRECTUS_API_URL}/items/Locations" \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer ${token}" \ - -d '{ - "id": "2", - "name": "New EVerst", - "coordinates": { - "type": "Point", - "coordinates": [-74.0620872, 41.041548] - } - }' | tee /dev/tty && echo) - - local location_id=$(jq -r '.data.id' <<< "$response") - echo "$location_id" - } - - # Function to add a charging station - add_charging_station() { - local token=$1 - local location_id=$2 - local chargepointId=$3 - curl -s --request POST \ - --url "${DIRECTUS_API_URL}/items/ChargingStations" \ - --header "Authorization: Bearer $token" \ - --header "Content-Type: application/json" \ - --data '{ - "id": "'"$chargepointId"'", - "locationId": "'"$location_id"'" - }' | tee /dev/tty && echo - } - - - # Function to update SP1 password - add_cp001_password() { - local response - local success=false - local attempt=1 - local passwordString=$1 - - until $success; do - echo "Attempt $attempt: Updating SP1 password..." - response=$(curl -s -o /dev/null -w "%{http_code}" --location --request PUT "http://localhost:8080/data/monitoring/variableAttribute?stationId=${CHARGEPOINT_ID}&setOnCharger=true" \ - --header "Content-Type: application/json" \ - --data-raw '{ - "component": { - "name": "SecurityCtrlr" - }, - "variable": { - "name": "BasicAuthPassword" - }, - "variableAttribute": [ - { - "value": "'"$passwordString"'" - } - ], - "variableCharacteristics": { - "dataType": "passwordString", - "supportsMonitoring": false - } - }' | tee /dev/tty) - - - if [[ $response -ge 200 && $response -lt 300 ]]; then - echo "Password update successful." - success=true - else - echo "Password update failed with HTTP status: $response. Retrying in 2 second..." - sleep 2 - ((attempt++)) - fi - done - } - - # Main script execution - TOKEN=$(get_directus_token) - echo "Received Token: $TOKEN" - - if [ -z "$TOKEN" ]; then - echo "Failed to retrieve access token." - exit 1 - fi - - echo "Adding a new location..." - LOCATION_ID=$(add_location "$TOKEN") - - if [ -z "$LOCATION_ID" ]; then - echo "Failed to add new location." - exit 1 - fi - - echo "Location ID: $LOCATION_ID" - - echo "Adding new station..." - add_charging_station "$TOKEN" "$LOCATION_ID" "$CHARGEPOINT_ID" - - echo "Add cp001 password to citrine..." - add_cp001_password "$CP_PASSWORD" - else - if [[ "$DEMO_VERSION" =~ sp1 ]]; then - echo "MaEVe CSMS started, adding charge station with Security Profile 1 (note: profiles in MaEVe start with 0 so SP-0 == OCPP SP-1)" - curl http://localhost:9410/api/v0/cs/cp001 -H 'content-type: application/json' \ - -d '{"securityProfile": 0, "base64SHA256Password": "3oGi4B5I+Y9iEkYtL7xvuUxrvGOXM/X2LQrsCwf/knA="}' - elif [[ "$DEMO_VERSION" =~ sp2 ]]; then - echo "MaEVe CSMS started, adding charge station with Security Profile 2 (note: profiles in MaEVe start with 0 so SP-1 == OCPP SP-2)" - curl http://localhost:9410/api/v0/cs/cp001 -H 'content-type: application/json' \ - -d '{"securityProfile": 1, "base64SHA256Password": "3oGi4B5I+Y9iEkYtL7xvuUxrvGOXM/X2LQrsCwf/knA="}' - elif [[ "$DEMO_VERSION" =~ sp3 ]]; then - echo "MaEVe CSMS started, adding charge station with Security Profile 3 (note: profiles in MaEVe start with 0 so SP-2 == OCPP SP-3)" - curl http://localhost:9410/api/v0/cs/cp001 -H 'content-type: application/json' -d '{"securityProfile": 2}' - fi - - echo "Charge station added, adding user token" - - curl http://localhost:9410/api/v0/token -H 'content-type: application/json' -d '{ - "countryCode": "GB", - "partyId": "TWK", - "type": "RFID", - "uid": "DEADBEEF", - "contractId": "GBTWK012345678V", - "issuer": "Thoughtworks", - "valid": true, - "cacheMode": "ALWAYS" - }' - - curl http://localhost:9410/api/v0/token -H 'content-type: application/json' -d '{"countryCode": "UK", "partyId": "Switch", "contractId": "UKSWI123456789G", "uid": "UKSWI123456789G", "issuer": "Switch", "valid": true, "cacheMode": "ALWAYS"}' - fi - - echo "API calls to CSMS finished, starting EVerest..." + curl http://localhost:9410/api/v0/token -H 'content-type: application/json' -d '{ + "countryCode": "GB", + "partyId": "TWK", + "type": "RFID", + "uid": "DEADBEEF", + "contractId": "GBTWK012345678V", + "issuer": "Thoughtworks", + "valid": true, + "cacheMode": "ALWAYS" + }' + + curl http://localhost:9410/api/v0/token -H 'content-type: application/json' -d '{"countryCode": "UK", "partyId": "Switch", "contractId": "UKSWI123456789G", "uid": "UKSWI123456789G", "issuer": "Switch", "valid": true, "cacheMode": "ALWAYS"}' popd || exit 1 fi -pushd everest-demo || exit 1 -docker compose --project-name everest-ac-demo --file "${DEMO_COMPOSE_FILE_NAME}" up -d --wait +if [[ "$DEMO_VERSION" != v1.6j && "$DEMO_CSMS" == 'citrineos' ]]; then + echo "Cloning CitrineOS CSMS from ${CITRINEOS_REPO} into ${DEMO_DIR}/citrineos-csms and starting it" + git clone --branch "${CITRINEOS_BRANCH}" "${CITRINEOS_REPO}" citrineos-csms -# Configure and restart nodered -docker cp nodered/config/config-sil-iso15118-ac-flow.json everest-ac-demo-nodered-1:/config/config-sil-two-evse-flow.json -docker restart everest-ac-demo-nodered-1 + pushd citrineos-csms || exit 1 -# Configure and restart EVerest -docker cp config-sil-ocpp201-pnc.yaml everest-ac-demo-manager-1:/ext/source/config/config-sil-ocpp201-pnc.yaml -docker cp manager/enable_payment_method.patch everest-ac-demo-manager-1:/tmp/ -docker cp manager/enable_evcc_logging.cfg everest-ac-demo-manager-1:/ext/source/build/dist/etc/everest/default_logging.cfg -docker exec everest-ac-demo-manager-1 /bin/bash -c "apk add patch && cd /ext && patch -p0 -i /tmp/enable_payment_method.patch" + cp ../everest-demo/manager/cached_certs_correct_name_emaid.tar.gz . + + mkdir -p Server/data/certificates + + echo "Copying certs into ${DEMO_DIR}/citrineos-csms/Server/data/certificates" + tar xf cached_certs_correct_name_emaid.tar.gz + + # Leaf key + cp dist/etc/everest/certs/client/csms/CSMS_LEAF.key Server/data/certificates/leafKey.pem + + #Cert chain + cat dist/etc/everest/certs/client/csms/CSMS_LEAF.pem \ + dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.pem \ + dist/etc/everest/certs/ca/csms/CPO_SUB_CA1.pem \ + > Server/data/certificates/certChain.pem + # SubCA + cp dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.key Server/data/certificates/subCAKey.pem + + #TrustedSubCAChain + cat dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.pem \ + dist/etc/everest/certs/ca/csms/CPO_SUB_CA1.pem \ + > Server/data/certificates/rootCertificate.pem + + #ACME key + cp ../everest-demo/citrineos/acme_account_key.pem Server/data/certificates/acme_account_key.pem + + pushd Server || exit 1 + echo "Starting the CitrineOS CSMS" + cat ./docker-compose.yml + docker compose -f ./docker-compose.yml build + if ! docker compose --project-name citrineos-csms -f ./docker-compose.yml up -d --wait; then + echo "Failed to start CitrineOS." + exit 1 + fi + + echo "Adding a charger and RFID card to CitrineOS" + ../../everest-demo/citrineos/add-charger-and-rfid-card.sh + + popd || exit 1 + popd || exit 1 + +fi + +pushd everest-demo || exit 1 +echo "API calls to CSMS finished, Starting everest" +docker compose --project-name everest-ac-demo --file "${DEMO_COMPOSE_FILE_NAME}" up -d --wait +docker cp config-sil-ocpp201-pnc.yaml everest-ac-demo-manager-1:/ext/source/config/config-sil-ocpp201-pnc.yaml if [[ "$DEMO_VERSION" =~ sp2 || "$DEMO_VERSION" =~ sp3 ]]; then docker cp manager/cached_certs_correct_name_emaid.tar.gz everest-ac-demo-manager-1:/ext/source/build docker exec everest-ac-demo-manager-1 /bin/bash -c "pushd /ext/source/build && tar xf cached_certs_correct_name_emaid.tar.gz" @@ -364,9 +239,7 @@ if [[ "$DEMO_VERSION" =~ sp2 || "$DEMO_VERSION" =~ sp3 ]]; then docker exec everest-ac-demo-manager-1 /bin/bash -c "pushd /ext/source/build && openssl verify -show_chain -CAfile dist/etc/everest/certs/ca/v2g/V2G_ROOT_CA.pem --untrusted dist/etc/everest/certs/ca/csms/CPO_SUB_CA1.pem --untrusted dist/etc/everest/certs/ca/csms/CPO_SUB_CA2.pem dist/etc/everest/certs/client/csms/CSMS_LEAF.pem" fi -if [[ ${CSMS} == "citrine" && ! ("$DEMO_VERSION" =~ sp1) ]]; then - echo "TODO: Set up device model correctly!" -else +if [[ "$DEMO_CSMS" == 'maeve' ]]; then if [[ "$DEMO_VERSION" =~ sp1 ]]; then echo "Copying device DB, configured to SecurityProfile: 1" docker cp manager/device_model_storage_maeve_sp1.db \ @@ -382,7 +255,23 @@ else fi fi +if [[ "$DEMO_CSMS" == 'citrineos' ]]; then + if [[ "$DEMO_VERSION" =~ sp1 ]]; then + echo "Copying device DB, configured to SecurityProfile: 1" + docker cp manager/device_model_storage_citrineos_sp1.db \ + everest-ac-demo-manager-1:/ext/source/build/dist/share/everest/modules/OCPP201/device_model_storage.db + elif [[ "$DEMO_VERSION" =~ sp2 ]]; then + echo "Copying device DB, configured to SecurityProfile: 2" + docker cp manager/device_model_storage_citrineos_sp2.db \ + everest-ac-demo-manager-1:/ext/source/build/dist/share/everest/modules/OCPP201/device_model_storage.db + elif [[ "$DEMO_VERSION" =~ sp3 ]]; then + echo "Copying device DB, configured to SecurityProfile: 3" + docker cp manager/device_model_storage_citrineos_sp3.db \ + everest-ac-demo-manager-1:/ext/source/build/dist/share/everest/modules/OCPP201/device_model_storage.db + fi +fi + if [[ "$DEMO_VERSION" =~ v2.0.1 ]]; then echo "Starting software in the loop simulation" docker exec everest-ac-demo-manager-1 sh /ext/source/build/run-scripts/run-sil-ocpp201-pnc.sh -fi +fi \ No newline at end of file diff --git a/manager/device_model_storage_citrineos_sp1.db b/manager/device_model_storage_citrineos_sp1.db new file mode 100644 index 00000000..67b9838d Binary files /dev/null and b/manager/device_model_storage_citrineos_sp1.db differ diff --git a/manager/device_model_storage_citrineos_sp2.db b/manager/device_model_storage_citrineos_sp2.db new file mode 100644 index 00000000..6a9adf59 Binary files /dev/null and b/manager/device_model_storage_citrineos_sp2.db differ diff --git a/manager/device_model_storage_citrineos_sp3.db b/manager/device_model_storage_citrineos_sp3.db new file mode 100644 index 00000000..16c61d64 Binary files /dev/null and b/manager/device_model_storage_citrineos_sp3.db differ