From 3bf2e2db12678ff1026b4e38ca60b5b7e2c20b80 Mon Sep 17 00:00:00 2001 From: the-bay-kay Date: Wed, 12 Jun 2024 15:51:36 -0700 Subject: [PATCH 1/9] Added Option to run ISO-15118 + OPP with Edgeshark - Added -s flag to `demo-iso15118-2-ac-plus-ocpp.sh`, to curl and launch EdgeShark alongside demo - Added "Optional" section to README, to explain this flag Signed-off-by: the-bay-kay --- README.md | 7 +++++++ demo-iso15118-2-ac-plus-ocpp.sh | 17 +++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index af2faf90..6e82d21a 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,13 @@ in your terminal before one of the one-liners presented in the next section. - Run the demo: 💄 exploring configs 🔧: `curl -o docker-compose.yml https://raw.githubusercontent.com/everest/everest-demo/main/docker-compose.admin-panel.yml && docker compose -p everest-admin-panel up` - Access the visual representation at http://localhost:8849 +### OPTIONAL: Explore a demo with Edgeshark +- For each demo of ISO 15118-2 AC Charging with OCPP, the demo may be ran alongside [Edgeshark](httpos://github.com/siemans/edgeshark?tab=readme-ov-file#edgeshark) for additional information on the network connection between each container. +- To launch a Edgeshark alongside one of these demos, include the -s flag when running the demo + - 🦈 Example Profile 2 ⚡: `curl https://raw.githubusercontent.com/everest/everest-demo/main/demo-iso15118-2-ac-plus-ocpp.sh | bash -s - -s - -2` +- Using your preferred web browser, navigate to http://localhost:5001 and explore the connections via the discovery engine. + - If you have installed the [cshargextcap plugin](https://github.com/siemens/cshargextcap?tab=readme-ov-file#containershark-extcap-plugin-for-wireshark), you may launch [Wireshark Desktop](https://www.wireshark.org/download.html) from the discovery engine, to perform a live capture. + ### TEARDOWN: Clean up after the demo - Kill the demo process - Delete files and containers: `docker compose -p [prefix] down && rm docker-compose.yml` diff --git a/demo-iso15118-2-ac-plus-ocpp.sh b/demo-iso15118-2-ac-plus-ocpp.sh index 8c599e10..d0e56e9b 100755 --- a/demo-iso15118-2-ac-plus-ocpp.sh +++ b/demo-iso15118-2-ac-plus-ocpp.sh @@ -10,7 +10,7 @@ CSMS="maeve" -usage="usage: $(basename "$0") [-r ] [-b ] [-c ] [-j|1|2|3] [-h] +usage="usage: $(basename "$0") [-r ] [-b ] [-c ] [-s] [-j|1|2|3] [-h] This script will run EVerest ISO 15118-2 AC charging with OCPP demos. @@ -21,6 +21,7 @@ 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) + -s Run with Edgeshark -j OCPP v1.6j -1 OCPP v2.0.1 Security Profile 1 -2 OCPP v2.0.1 Security Profile 2 @@ -30,16 +31,17 @@ where: DEMO_VERSION= DEMO_COMPOSE_FILE_NAME= - +RUN_WITH_EDGESHARK=false # loop through positional options/arguments -while getopts ':r:b:cj123h' option; do +while getopts ':r:b:c:sj123h' 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" ;; + s) RUN_WITH_EDGESHARK=true ;; j) DEMO_VERSION="v1.6j" DEMO_COMPOSE_FILE_NAME="docker-compose.ocpp16j.yml" ;; 1) DEMO_VERSION="v2.0.1-sp1" @@ -84,13 +86,20 @@ echo "DEMO VERSION: $DEMO_VERSION" echo "DEMO CONFIG: $DEMO_COMPOSE_FILE_NAME" echo "DEMO DIR: $DEMO_DIR" - 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 +echo "Run with Edgeshark? $RUN_WITH_EDGESHARK" + +if [[ "$RUN_WITH_EDGESHARK" = true ]]; then + wget -q --no-cache -O - \ + https://github.com/siemens/edgeshark/raw/main/deployments/wget/docker-compose-localhost.yaml \ + | docker compose -f - up -d +fi + if [[ "$DEMO_VERSION" != v1.6j ]]; then echo "Cloning ${CSMS} CSMS from ${CSMS_REPO} into ${DEMO_DIR}/${CSMS}-csms and starting it" git clone ${CSMS_REPO} ${CSMS}-csms From 36a9b46b8c3e753b0dd929a48a9c4dd1a067218c Mon Sep 17 00:00:00 2001 From: the-bay-kay Date: Wed, 12 Jun 2024 16:07:43 -0700 Subject: [PATCH 2/9] Fixed one-line Edgeshark demo - Args were Out of Order, fixed. Signed-off-by: the-bay-kay --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e82d21a..cada660a 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ in your terminal before one of the one-liners presented in the next section. ### OPTIONAL: Explore a demo with Edgeshark - For each demo of ISO 15118-2 AC Charging with OCPP, the demo may be ran alongside [Edgeshark](httpos://github.com/siemans/edgeshark?tab=readme-ov-file#edgeshark) for additional information on the network connection between each container. - To launch a Edgeshark alongside one of these demos, include the -s flag when running the demo - - 🦈 Example Profile 2 ⚡: `curl https://raw.githubusercontent.com/everest/everest-demo/main/demo-iso15118-2-ac-plus-ocpp.sh | bash -s - -s - -2` + - 🦈 Example Profile 2 ⚡: `curl https://raw.githubusercontent.com/everest/everest-demo/main/demo-iso15118-2-ac-plus-ocpp.sh | bash -s -- -s -2` - Using your preferred web browser, navigate to http://localhost:5001 and explore the connections via the discovery engine. - If you have installed the [cshargextcap plugin](https://github.com/siemens/cshargextcap?tab=readme-ov-file#containershark-extcap-plugin-for-wireshark), you may launch [Wireshark Desktop](https://www.wireshark.org/download.html) from the discovery engine, to perform a live capture. From f6e14c5b123adcd4e73604d1bfae6b9d2be14f17 Mon Sep 17 00:00:00 2001 From: louisg1337 Date: Tue, 4 Jun 2024 16:14:42 -0400 Subject: [PATCH 3/9] Added in script to set charging profile on Citrine Signed-off-by: louisg1337 Signed-off-by: the-bay-kay --- demo-scripts/citrine-set-charging-profile.sh | 101 +++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 demo-scripts/citrine-set-charging-profile.sh diff --git a/demo-scripts/citrine-set-charging-profile.sh b/demo-scripts/citrine-set-charging-profile.sh new file mode 100644 index 00000000..41488d71 --- /dev/null +++ b/demo-scripts/citrine-set-charging-profile.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# Check if the correct number of arguments are provided +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Assign arguments to variables +IDENTIFIER=$1 +TENANT_ID=$2 + +echo "setChargingProfile called with Identifier: ${IDENTIFIER} and tenandId: ${TENANT_ID}" + +curl -X 'POST' \ + "http://localhost:8080/ocpp/smartcharging/setChargingProfile?identifier=${IDENTIFIER}&tenantId=${TENANT_ID}" \ + -H 'accept: */*' \ + -H 'Content-Type: application/json' \ + -d '{ + "customData": { + "vendorId": "string" + }, + "evseId": 0, + "chargingProfile": { + "customData": { + "vendorId": "string" + }, + "id": 0, + "stackLevel": 0, + "chargingProfilePurpose": "ChargingStationExternalConstraints", + "chargingProfileKind": "Absolute", + "recurrencyKind": "Daily", + "validFrom": "2024-06-03T21:58:35.240Z", + "validTo": "2024-06-03T21:58:35.240Z", + "chargingSchedule": [ + { + "customData": { + "vendorId": "string" + }, + "id": 0, + "startSchedule": "2024-06-03T21:58:35.240Z", + "duration": 0, + "chargingRateUnit": "W", + "chargingSchedulePeriod": [ + { + "customData": { + "vendorId": "string" + }, + "startPeriod": 0, + "limit": 0, + "numberPhases": 0, + "phaseToUse": 0 + } + ], + "minChargingRate": 0, + "salesTariff": { + "customData": { + "vendorId": "string" + }, + "id": 0, + "salesTariffDescription": "string", + "numEPriceLevels": 0, + "salesTariffEntry": [ + { + "customData": { + "vendorId": "string" + }, + "relativeTimeInterval": { + "customData": { + "vendorId": "string" + }, + "start": 0, + "duration": 0 + }, + "ePriceLevel": 0, + "consumptionCost": [ + { + "customData": { + "vendorId": "string" + }, + "startValue": 0, + "cost": [ + { + "customData": { + "vendorId": "string" + }, + "costKind": "CarbonDioxideEmission", + "amount": 0, + "amountMultiplier": 0 + } + ] + } + ] + } + ] + } + } + ], + "transactionId": "string" + } +}' From 20761ccab2b3cf0f5f5605723239c4e3a7f3312e Mon Sep 17 00:00:00 2001 From: Shankari Date: Tue, 4 Jun 2024 20:50:54 -0700 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=90=9BFix=20citrine=20test=20after=20?= =?UTF-8?q?upgrade=20to=200.0.16?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes https://github.com/EVerest/everest-demo/issues/50 by copying over the device model for Citrine SP1 as well. Note that the location where the device model is read is different after 0.0.16 https://github.com/EVerest/everest-demo/issues/50#issuecomment-2148622907 Testing done: ``` $ bash demo-iso15118-2-ac-plus-ocpp.sh -c -1 /var/folders/y5/cx3cfzrd2q116myv9ly86sw1rnlmdj/T/tmp.hzWe2rEL/citrine-csms /var/folders/y5/cx3cfzrd2q116myv9ly86sw1rnlmdj/T/tmp.hzWe2rEL HEAD is now at 63670f3 Merge pull request #99 from citrineos/fix/fix-container-build-pipeline ... ✔ Network server_default Created 0.1s ✔ Container server-amqp-broker-1 Created 0.1s ✔ Container server-ocpp-db-1 Healthy 0.1s ✔ Container server-directus-1 Created 0.1s ✔ Container server-citrine-1 Created 10.9s ... 200Password update successful. API calls to CSMS finished, starting EVerest... ... [+] Running 4/4 ✔ Network everest-ac-demo_default Created 0.0s ✔ Container everest-ac-demo-mqtt-server-1 Healthy 0.1s ✔ Container everest-ac-demo-manager-1 Healthy 0.1s ✔ Container everest-ac-demo-nodered-1 Healthy 0.1s Successfully copied 6.66kB to everest-ac-demo-manager-1:/ext/source/config/config-sil-ocpp201-pnc.yaml Copying device DB, configured to SecurityProfile: 1 Successfully copied 84.5kB to everest-ac-demo-manager-1:/ext/source/build/dist/share/everest/modules/OCPP201/device_model_storage.db 2024-06-05 03:46:08.957953 [INFO] ocpp:OCPP201 :: Connecting to plain websocket at uri: ws://host.docker.internal/ws/cp001 with security profile: 1 2024-06-05 03:46:09.053553 [INFO] ocpp:OCPP201 :: OCPP client successfully connected to plain websocket server 2024-06-05 03:47:19.161544 [INFO] ocpp:OCPP201 :: Received BootNotificationResponse: { "currentTime": "2024-06-05T03:47:19.157Z", "interval": 60, "status": "Accepted" } ``` Signed-off-by: the-bay-kay --- demo-iso15118-2-ac-plus-ocpp.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-iso15118-2-ac-plus-ocpp.sh b/demo-iso15118-2-ac-plus-ocpp.sh index d0e56e9b..fff2a597 100755 --- a/demo-iso15118-2-ac-plus-ocpp.sh +++ b/demo-iso15118-2-ac-plus-ocpp.sh @@ -358,7 +358,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" ]]; then +if [[ ${CSMS} == "citrine" && ! ("$DEMO_VERSION" =~ sp1) ]]; then echo "TODO: Set up device model correctly!" else if [[ "$DEMO_VERSION" =~ sp1 ]]; then From a8be0aacb2d5349165f71937432f2993a64ceafb Mon Sep 17 00:00:00 2001 From: louisg1337 Date: Sat, 8 Jun 2024 11:44:39 -0400 Subject: [PATCH 5/9] Fixed demo script to point to my MaEVe branch that supports set charging profile. Also added a script that will make that API request for us Signed-off-by: louisg1337 Signed-off-by: the-bay-kay --- demo-iso15118-2-ac-plus-ocpp.sh | 8 ++- demo-scripts/maeve-set-charging-profile.sh | 66 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 demo-scripts/maeve-set-charging-profile.sh diff --git a/demo-iso15118-2-ac-plus-ocpp.sh b/demo-iso15118-2-ac-plus-ocpp.sh index fff2a597..569360d8 100755 --- a/demo-iso15118-2-ac-plus-ocpp.sh +++ b/demo-iso15118-2-ac-plus-ocpp.sh @@ -4,8 +4,9 @@ DEMO_REPO="https://github.com/everest/everest-demo.git" DEMO_BRANCH="main" -CSMS_REPO="https://github.com/thoughtworks/maeve-csms.git" -CSMS_BRANCH="b990d0eddf2bf80be8d9524a7b08029fbb305c7d" # patch files are based on this commit +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" @@ -102,7 +103,7 @@ fi if [[ "$DEMO_VERSION" != v1.6j ]]; then echo "Cloning ${CSMS} CSMS from ${CSMS_REPO} into ${DEMO_DIR}/${CSMS}-csms and starting it" - git clone ${CSMS_REPO} ${CSMS}-csms + git clone --branch "${CSMS_BRANCH}" "${CSMS_REPO}" ${CSMS}-csms pushd ${CSMS}-csms || exit 1 @@ -185,6 +186,7 @@ if [[ "$DEMO_VERSION" != v1.6j ]]; then fi fi + docker compose build docker compose up -d echo "Waiting 5s for CSMS to start..." diff --git a/demo-scripts/maeve-set-charging-profile.sh b/demo-scripts/maeve-set-charging-profile.sh new file mode 100644 index 00000000..c0aaca53 --- /dev/null +++ b/demo-scripts/maeve-set-charging-profile.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +CS=$1 + +echo "setChargingProfile called with Charging Station: ${CS}" + +curl -X POST \ + "http://localhost:9410/api/v0/cs/${CS}/setchargingprofile" \ + -H "Content-Type: application/json" \ + -d '{ + "chargingProfileKind": "Absolute", + "chargingProfilePurpose": "TxProfile", + "chargingSchedule": [ + { + "chargingRateUnit": "W", + "chargingSchedulePeriod": [ + { + "limit": 22.5, + "startPeriod": 0, + "numberPhases": 3 + }, + { + "limit": 20.0, + "startPeriod": 3600, + "numberPhases": 3 + } + ], + "id": 1, + "minChargingRate": 5.0, + "salesTariff": { + "id": 1, + "numEPriceLevels": 2, + "salesTariffDescription": "Standard Tariff", + "salesTariffEntry": [ + { + "relativeTimeInterval": { + "start": 0, + "duration": 3600 + }, + "consumptionCost": [ + { + "cost": [ + { + "amount": 15, + "costKind": "RelativePricePercentage" + } + ], + "startValue": 10.0 + } + ] + } + ] + } + } + ], + "id": 1, + "stackLevel": 0, + "transactionId": "12345", + "validFrom": "2024-06-07T10:00:00Z", + "validTo": "2024-06-07T18:00:00Z" + }' From 9898cc393c7498a97b7b89a9a51f0d3a1a045513 Mon Sep 17 00:00:00 2001 From: louisg1337 Date: Sat, 8 Jun 2024 12:57:37 -0400 Subject: [PATCH 6/9] Fixed git cloning issue. MaEVe depends on my branch, while Citrine depends on specific SHA of a commit on the main, so handle accordingly Signed-off-by: louisg1337 Signed-off-by: the-bay-kay --- demo-iso15118-2-ac-plus-ocpp.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/demo-iso15118-2-ac-plus-ocpp.sh b/demo-iso15118-2-ac-plus-ocpp.sh index 569360d8..2c45901a 100755 --- a/demo-iso15118-2-ac-plus-ocpp.sh +++ b/demo-iso15118-2-ac-plus-ocpp.sh @@ -103,7 +103,12 @@ fi if [[ "$DEMO_VERSION" != v1.6j ]]; then echo "Cloning ${CSMS} CSMS from ${CSMS_REPO} into ${DEMO_DIR}/${CSMS}-csms and starting it" - git clone --branch "${CSMS_BRANCH}" "${CSMS_REPO}" ${CSMS}-csms + + if [[ ${CSMS} == "maeve" ]]; then + git clone --branch "${CSMS_BRANCH}" "${CSMS_REPO}" ${CSMS}-csms + else + git clone ${CSMS_REPO} ${CSMS}-csms + fi pushd ${CSMS}-csms || exit 1 From 3f22b7d4d136c6a367a35ed81bf6af72a5ed5d03 Mon Sep 17 00:00:00 2001 From: Shankari Date: Sun, 9 Jun 2024 12:10:33 -0700 Subject: [PATCH 7/9] Fix the `ignore-ocsp` patch to work with the new tag The line numbers have changed and the way that the status is handled is different Testing done: ``` Patching the CSMS to enable local mo root patching file 'manager/handlers/ocpp201/authorize.go' Starting the CSMS ``` Signed-off-by: Shankari Signed-off-by: the-bay-kay --- maeve/maeve-csms-ignore-ocsp.patch | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/maeve/maeve-csms-ignore-ocsp.patch b/maeve/maeve-csms-ignore-ocsp.patch index d0e746f7..a39e1db8 100644 --- a/maeve/maeve-csms-ignore-ocsp.patch +++ b/maeve/maeve-csms-ignore-ocsp.patch @@ -2,29 +2,29 @@ diff --git a/manager/handlers/ocpp201/authorize.go b/manager/handlers/ocpp201/au index 5df2305..0db9f79 100644 --- a/manager/handlers/ocpp201/authorize.go +++ b/manager/handlers/ocpp201/authorize.go -@@ -49,7 +49,12 @@ func (a AuthorizeHandler) HandleCall(ctx context.Context, chargeStationId string +@@ -38,7 +38,12 @@ func (a AuthorizeHandler) HandleCall(ctx context.Context, chargeStationId string if req.Certificate != nil { _, err = a.CertificateValidationService.ValidatePEMCertificateChain(ctx, []byte(*req.Certificate), req.IdToken.IdToken) - status, certificateStatus = handleCertificateValidationError(err) + idTokenInfo.Status, certificateStatus = handleCertificateValidationError(err) - if err != nil { + if err.Error() == "failed to perform ocsp check after 1 attempts" { + var tempStatus = types.AuthorizeCertificateStatusEnumTypeAccepted + certificateStatus = &tempStatus -+ status = types.AuthorizationStatusEnumTypeAccepted ++ idTokenInfo.Status = types.AuthorizationStatusEnumTypeAccepted + span.SetAttributes(attribute.String("authorize.cert_warn", "No OCSP, but ignoring for testing purpose.")) + } else if err != nil { span.SetAttributes(attribute.String("authorize.cert_error", err.Error())) } } -@@ -57,7 +62,12 @@ func (a AuthorizeHandler) HandleCall(ctx context.Context, chargeStationId string +@@ -46,7 +46,12 @@ func (a AuthorizeHandler) HandleCall(ctx context.Context, chargeStationId string if req.Iso15118CertificateHashData != nil { _, err := a.CertificateValidationService.ValidateHashedCertificateChain(ctx, *req.Iso15118CertificateHashData) - status, certificateStatus = handleCertificateValidationError(err) + idTokenInfo.Status, certificateStatus = handleCertificateValidationError(err) - if err != nil { + if err.Error() == "failed to perform ocsp check after 1 attempts" { + var tempStatus = types.AuthorizeCertificateStatusEnumTypeAccepted + certificateStatus = &tempStatus -+ status = types.AuthorizationStatusEnumTypeAccepted ++ idTokenInfo.Status = types.AuthorizationStatusEnumTypeAccepted + span.SetAttributes(attribute.String("authorize.cert_warn", "No OCSP, but ignoring for testing purpose.")) + } else if err != nil { span.SetAttributes(attribute.String("authorize.cert_error", err.Error())) From c6933ef06b23a7e68922afeb3e4daba947989368 Mon Sep 17 00:00:00 2001 From: Shankari Date: Sun, 9 Jun 2024 12:43:22 -0700 Subject: [PATCH 8/9] Enable AC ISO 15118 properly with two payment modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use a nodered flow that only supports AC ISO 15118-2 EIM and PnC - Patch SwitchEV to plumb through the payment option to the ISO 15118 library by following the EnergyTransfer route https://github.com/EVerest/everest-demo/issues/44#issuecomment-2150633370 https://lfenergy.zulipchat.com/#narrow/stream/417677-EVerest.3A-Car-com.2E/topic/.E2.9C.94.20Update.20Node-red.20flows.20because.20of.20JsEvManager.20commit/near/443617217 - Enable `iso15118car` debug logging so that we can see the decoded EXI messages in the absence of https://lfenergy.zulipchat.com/#narrow/stream/417677-EVerest.3A-Car-com.2E/topic/EXI.20V2G.20Decoder.20Recommendations/near/442318988 - disable the maeve patches to remove the LB since it is already removed in the updated branch Testing done: ``` patching file docker-compose.yml Patching the CSMS to enable local mo root patching file 'config/manager/config.toml' Patching the CSMS to enable local mo root patching file 'manager/handlers/ocpp201/authorize.go' Starting the CSMS ``` ``` [+] Running 4/4 ✔ Container maeve-csms-firestore-1 Running 0.0s ✔ Container maeve-csms-mqtt-1 Healthy 0.1s ✔ Container maeve-csms-manager-1 Healthy 0.2s ✔ Container maeve-csms-gateway-1 Started 0.2s Waiting 5s for CSMS to start... MaEVe CSMS started, adding charge station with Security Profile 3 (note: profiles in MaEVe start with 0 so SP-2 == OCPP SP-3) Charge station added, adding user token API calls to CSMS finished, starting EVerest... ``` ``` ✔ Network everest-ac-demo_default Created 0.1s ✔ Container everest-ac-demo-mqtt-server-1 Healthy 0.1s ✔ Container everest-ac-demo-nodered-1 Healthy 0.2s ✔ Container everest-ac-demo-manager-1 Healthy 0.1s Successfully copied 60.9kB to everest-ac-demo-nodered-1:/config/config-sil-two-evse-flow.json everest-ac-demo-nodered-1 Successfully copied 6.66kB to everest-ac-demo-manager-1:/ext/source/config/config-sil-ocpp201-pnc.yaml Successfully copied 8.19kB to everest-ac-demo-manager-1:/tmp/ Successfully copied 3.58kB to everest-ac-demo-manager-1:/ext/source/build/dist/etc/everest/default_logging.cfg fetch https://dl-cdn.alpinelinux.org/alpine/v3.17/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.17/community/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/APKINDEX.tar.gz (1/1) Installing patch (2.7.6-r9) Executing busybox-1.35.0-r29.trigger OK: 1379 MiB in 230 packages patching file source/build/dist/libexec/everest/3rd_party/josev/iso15118/evcc/controller/interface.py patching file source/build/dist/libexec/everest/3rd_party/josev/iso15118/evcc/controller/simulator.py patching file source/build/dist/libexec/everest/3rd_party/josev/iso15118/evcc/states/iso15118_2_states.py Successfully copied 29.2kB to everest-ac-demo-manager-1:/ext/source/build ``` ``` 2024-06-09 19:41:31.667368 [DEBG] iso15118_car pybind11_init_everestpy(pybind11::module_&):: :: Decoded message (ns=urn:iso:15118:2:2013:MsgDef): {"V2G_Message":{"Header":{"SessionID":"04337FC4777FB77D"},"Body":{"ServiceDiscoveryRes":{"ResponseCode":"OK","PaymentOptionList":{"PaymentOption":["ExternalPayment","Contract"]},"ChargeService":{"ServiceID":1,"ServiceCategory":"EVCharging","FreeService":false,"SupportedEnergyTransferMode":{"EnergyTransferMode":["AC_single_phase_core","AC_three_phase_core"]}},"ServiceList":{"Service":[{"ServiceID":2,"ServiceName":"Certificate","ServiceCategory":"ContractCertificate","FreeService":true}]}}}}} 2024-06-09 19:41:31.670311 [WARN] iso15118_car pybind11_init_everestpy(pybind11::module_&):: :: V2G_PAYMENT: in function read value from state AuthEnum.PNC_V2 2024-06-09 19:41:31.671413 [DEBG] iso15118_car pybind11_init_everestpy(pybind11::module_&):: :: Message to encode (ns=urn:iso:15118:2:2013:MsgDef): {"V2G_Message": {"Header": {"SessionID": "04337FC4777FB77D"}, "Body": {"PaymentServiceSelectionReq": {"SelectedPaymentOption": "Contract", "SelectedServiceList": {"SelectedService": [{"ServiceID": 1}]}}}}} 2024-06-09 19:41:34.155595 [INFO] ocpp:OCPP201 :: CSMS certificate status: Accepted 2024-06-09 19:41:34.159815 [INFO] auth:Auth :: Providing authorization to connector#1 2024-06-09 19:41:35.873697 [DEBG] iso15118_car pybind11_init_everestpy(pybind11::module_&):: :: Message to encode (ns=urn:iso:15118:2:2013:MsgDef): {"V2G_Message": {"Header": {"SessionID": "04337FC4777FB77D"}, "Body": {"ChargeParameterDiscoveryReq": {"RequestedEnergyTransferMode": "AC_three_phase_core", "AC_EVChargeParameter": {"DepartureTime": 0, "EAmount": {"Value": 60, "Multiplier": 0, "Unit": "Wh"}, "EVMaxVoltage": {"Value": 400, "Multiplier": 0, "Unit": "V"}, "EVMaxCurrent": {"Value": 32000, "Multiplier": -3, "Unit": "A"}, "EVMinCurrent": {"Value": 10, "Multiplier": 0, "Unit": "A"}}}}}} 2024-06-09 19:41:36.948664 [DEBG] iso15118_car pybind11_init_everestpy(pybind11::module_&):: :: Decoded message (ns=urn:iso:15118:2:2013:MsgDef): {"V2G_Message":{"Header":{"SessionID":"04337FC4777FB77D"},"Body":{"ChargeParameterDiscoveryRes":{"ResponseCode":"OK","EVSEProcessing":"Finished","SAScheduleList":{"SAScheduleTuple":[{"SAScheduleTupleID":1,"PMaxSchedule":{"PMaxScheduleEntry":[{"RelativeTimeInterval":{"start":0,"duration":86400},"PMax":{"Multiplier":0,"Unit":"W","Value":22080}}]}}]},"AC_EVSEChargeParameter":{"AC_EVSEStatus":{"NotificationMaxDelay":0,"EVSENotification":"None","RCD":false},"EVSENominalVoltage":{"Multiplier":-1,"Unit":"V","Value":2300},"EVSEMaxCurrent":{"Multiplier":-1,"Unit":"A","Value":320}}}}}} ``` Signed-off-by: Shankari Signed-off-by: the-bay-kay --- demo-iso15118-2-ac-plus-ocpp.sh | 14 +- manager/enable_evcc_logging.cfg | 24 + manager/enable_payment_method.patch | 114 + .../config/config-sil-iso15118-ac-flow.json | 2256 +++++++++++++++++ 4 files changed, 2405 insertions(+), 3 deletions(-) create mode 100644 manager/enable_evcc_logging.cfg create mode 100644 manager/enable_payment_method.patch create mode 100644 nodered/config/config-sil-iso15118-ac-flow.json diff --git a/demo-iso15118-2-ac-plus-ocpp.sh b/demo-iso15118-2-ac-plus-ocpp.sh index 2c45901a..980c54a8 100755 --- a/demo-iso15118-2-ac-plus-ocpp.sh +++ b/demo-iso15118-2-ac-plus-ocpp.sh @@ -92,6 +92,7 @@ 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 echo "Run with Edgeshark? $RUN_WITH_EDGESHARK" @@ -129,9 +130,6 @@ if [[ "$DEMO_VERSION" != v1.6j ]]; then fi else cp ../everest-demo/manager/cached_certs_correct_name_emaid.tar.gz . - - echo "Patching the CSMS to disable load balancer" - patch -p1 -i ../everest-demo/maeve/maeve-csms-no-lb.patch fi # Set up certificates for SP2 and SP3 @@ -356,7 +354,17 @@ fi pushd everest-demo || exit 1 docker compose --project-name everest-ac-demo --file "${DEMO_COMPOSE_FILE_NAME}" up -d --wait + +# 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 + +# 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" + 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" diff --git a/manager/enable_evcc_logging.cfg b/manager/enable_evcc_logging.cfg new file mode 100644 index 00000000..0a33bc6b --- /dev/null +++ b/manager/enable_evcc_logging.cfg @@ -0,0 +1,24 @@ +# for documentation on this file format see: +# https://www.boost.org/doc/libs/1_54_0/libs/log/doc/html/log/detailed/utilities.html#log.detailed.utilities.setup.filter_formatter + +[Core] +DisableLogging=false + +# To get debug logs of only one module, add the "%Process% contains" filter, e.g.: +# +# "(%Process% contains OCPP201 and %Severity% >= DEBG)" +# +# whereas "OCPP201" is the value of the field `active_modules.NAME.module` in the respective /config/config-*.yaml. +Filter="%Severity% >= INFO or (%Process% contains iso15118_car and %Severity% >= DEBG)" + +[Sinks.Console] +Destination=Console +# Filter="%Target% contains \"MySink1\"" +Format="%TimeStamp% [%Severity%] \033[1;32m%Process%\033[0m \033[1;36m%function%\033[0m \033[1;30m%file%:\033[0m\033[1;32m%line%\033[0m: %Message%" +Asynchronous=false +AutoFlush=true +SeverityStringColorDebug="\033[1;30m" +SeverityStringColorInfo="\033[1;37m" +SeverityStringColorWarning="\033[1;33m" +SeverityStringColorError="\033[1;31m" +SeverityStringColorCritical="\033[1;35m" diff --git a/manager/enable_payment_method.patch b/manager/enable_payment_method.patch new file mode 100644 index 00000000..d214972a --- /dev/null +++ b/manager/enable_payment_method.patch @@ -0,0 +1,114 @@ +--- ext-switchev-iso15118/iso15118/evcc/controller/interface.py ++++ source/build/dist/libexec/everest/3rd_party/josev/iso15118/evcc/controller/interface.py +@@ -109,6 +109,15 @@ + raise NotImplementedError + + @abstractmethod ++ async def get_selected_auth_option( ++ self, protocol: Protocol ++ ) -> AuthEnum: ++ """ ++ Gets the auth transfer mode requested for the current charging session. ++ """ ++ raise NotImplementedError ++ ++ @abstractmethod + async def get_energy_transfer_mode( + self, protocol: Protocol + ) -> EnergyTransferModeEnum: +--- ext-switchev-iso15118/iso15118/evcc/controller/simulator.py ++++ source/build/dist/libexec/everest/3rd_party/josev/iso15118/evcc/controller/simulator.py +@@ -53,6 +53,7 @@ + SAScheduleTupleEntry as SAScheduleTupleEntryDINSPEC, + ) + from iso15118.shared.messages.enums import ( ++ AuthEnum, + ControlMode, + DCEVErrorCode, + EnergyTransferModeEnum, +@@ -233,11 +234,18 @@ + logger.error(f"Invalid protocol '{protocol}', can't determine EVCCID") + raise InvalidProtocolError + ++ async def get_selected_auth_option( ++ self, protocol: Protocol ++ ) -> AuthEnum: ++ """Overrides EVControllerInterface.get_selected_auth_option().""" ++ return AuthEnum(EVEREST_EV_STATE.PaymentOption) ++ + async def get_energy_transfer_mode( + self, protocol: Protocol + ) -> EnergyTransferModeEnum: + """Overrides EVControllerInterface.get_energy_transfer_mode().""" + return EnergyTransferModeEnum(EVEREST_EV_STATE.EnergyTransferMode) ++ + + async def get_supported_energy_services(self) -> List[ServiceV20]: + """Overrides EVControllerInterface.get_energy_transfer_service().""" +--- ext-switchev-iso15118/iso15118/evcc/states/iso15118_2_states.py ++++ source/build/dist/libexec/everest/3rd_party/josev/iso15118/evcc/states/iso15118_2_states.py +@@ -193,8 +193,9 @@ + self.stop_state_machine("ChargeService not offered") + return + +- self.select_auth_mode(service_discovery_res.auth_option_list.auth_options) ++ logger.warn("received auth options list %s" % service_discovery_res.auth_option_list.auth_options) + await self.select_services(service_discovery_res) ++ await self.select_auth_mode(service_discovery_res.auth_option_list.auth_options) + await self.select_energy_transfer_mode() + + charge_service: ChargeService = service_discovery_res.charge_service +@@ -262,12 +263,13 @@ + self.comm_session.selected_energy_mode.value.startswith("AC") + ) + +- def select_auth_mode(self, auth_option_list: List[AuthEnum]): ++ async def select_auth_mode(self, auth_option_list: List[AuthEnum]): + """ + Check if an authorization mode (aka payment option in ISO 15118-2) was + saved from a previously paused communication session and reuse for + resumed session, otherwise request from EV controller. + """ ++ logger.warn("V2G_PAYMENT: in function received auth options list %s" % auth_option_list) + if evcc_settings.ev_session_context.selected_auth_option: + logger.debug( + "Reusing authorization option " +@@ -279,15 +281,30 @@ + ) + evcc_settings.ev_session_context.selected_auth_option = None + else: +- # Choose Plug & Charge (pnc) or External Identification Means (eim) +- # as the selected authorization option. The car manufacturer might +- # have a mechanism to determine a user-defined or default +- # authorization option. This implementation favors pnc, but +- # feel free to change if need be. +- if AuthEnum.PNC_V2 in auth_option_list and self.comm_session.is_tls: +- self.comm_session.selected_auth_option = AuthEnum.PNC_V2 ++ logger.warn("V2G_PAYMENT: about to read value from state") ++ self.comm_session.selected_auth_option = ( ++ await self.comm_session.ev_controller.get_selected_auth_option( ++ Protocol.ISO_15118_2 ++ ) ++ ) ++ logger.warn("V2G_PAYMENT: in function read value from state %s" % self.comm_session.selected_auth_option) ++ if self.comm_session.selected_auth_option is not None: ++ logger.debug( ++ "V2G_PAYMENT: Found Payment Option %s passed in from the PyJoseV module, using it" % self.comm_session.selected_auth_option ++ ) + else: +- self.comm_session.selected_auth_option = AuthEnum.EIM_V2 ++ logger.debug( ++ "V2G_PAYMENT: No previous paused session, no PaymentOption set, using TLS flag %s to decide auth method" % self.comm_session.is_tls ++ ) ++ # Choose Plug & Charge (pnc) or External Identification Means (eim) ++ # as the selected authorization option. The car manufacturer might ++ # have a mechanism to determine a user-defined or default ++ # authorization option. This implementation favors pnc, but ++ # feel free to change if need be. ++ if AuthEnum.PNC_V2 in auth_option_list and self.comm_session.is_tls: ++ self.comm_session.selected_auth_option = AuthEnum.PNC_V2 ++ else: ++ self.comm_session.selected_auth_option = AuthEnum.EIM_V2 + + async def select_services(self, service_discovery_res: ServiceDiscoveryRes): + """ diff --git a/nodered/config/config-sil-iso15118-ac-flow.json b/nodered/config/config-sil-iso15118-ac-flow.json new file mode 100644 index 00000000..6c31b43d --- /dev/null +++ b/nodered/config/config-sil-iso15118-ac-flow.json @@ -0,0 +1,2256 @@ +[ + { + "id": "9aafbf849d4d6e12", + "type": "tab", + "label": "Debug", + "disabled": false, + "info": "" + }, + { + "id": "e921db8897354328", + "type": "tab", + "label": "RFID", + "disabled": false, + "info": "" + }, + { + "id": "ed603c51db9dcbb9", + "type": "tab", + "label": "Connector 1", + "disabled": false, + "info": "" + }, + { + "id": "af1e1eeac9c4b704", + "type": "group", + "z": "ed603c51db9dcbb9", + "style": { + "stroke": "#999999", + "stroke-opacity": "1", + "fill": "none", + "fill-opacity": "1", + "label": true, + "label-position": "nw", + "color": "#a4a4a4" + }, + "nodes": [ + "1295e032d7ddbc20" + ], + "x": 1114, + "y": 439, + "w": 152, + "h": 82 + }, + { + "id": "7140803fb3989089", + "type": "ui_base", + "theme": { + "name": "theme-custom", + "lightTheme": { + "default": "#0094CE", + "baseColor": "#0094CE", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": true, + "reset": false + }, + "darkTheme": { + "default": "#097479", + "baseColor": "#097479", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": true, + "reset": false + }, + "customTheme": { + "name": "EVerest", + "default": "#4B7930", + "baseColor": "#2a62ac", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "reset": false + }, + "themeState": { + "base-color": { + "default": "#2a62ac", + "value": "#2a62ac", + "edited": true + }, + "page-titlebar-backgroundColor": { + "value": "#2a62ac", + "edited": false + }, + "page-backgroundColor": { + "value": "#111111", + "edited": false + }, + "page-sidebar-backgroundColor": { + "value": "#333333", + "edited": false + }, + "group-textColor": { + "value": "#4f88d4", + "edited": false + }, + "group-borderColor": { + "value": "#555555", + "edited": false + }, + "group-backgroundColor": { + "value": "#333333", + "edited": false + }, + "widget-textColor": { + "value": "#eeeeee", + "edited": false + }, + "widget-backgroundColor": { + "value": "#2a62ac", + "edited": false + }, + "widget-borderColor": { + "value": "#333333", + "edited": false + }, + "base-font": { + "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" + } + }, + "angularTheme": { + "primary": "indigo", + "accents": "blue", + "warn": "red", + "background": "grey", + "palette": "light" + } + }, + "site": { + "name": "EVerest", + "hideToolbar": "false", + "allowSwipe": "false", + "lockMenu": "false", + "allowTempTheme": "true", + "dateFormat": "DD.MM.YYYY", + "sizes": { + "sx": 48, + "sy": 48, + "gx": 6, + "gy": 6, + "cx": 6, + "cy": 6, + "px": 6, + "py": 6 + } + } + }, + { + "id": "fc8686af.48d178", + "type": "mqtt-broker", + "name": "", + "broker": "mqtt-server", + "port": "1883", + "clientid": "", + "usetls": false, + "protocolVersion": "4", + "keepalive": "60", + "cleansession": true, + "birthTopic": "", + "birthQos": "0", + "birthPayload": "", + "birthMsg": {}, + "closeTopic": "", + "closeQos": "0", + "closePayload": "", + "closeMsg": {}, + "willTopic": "", + "willQos": "0", + "willPayload": "", + "willMsg": {}, + "sessionExpiry": "" + }, + { + "id": "5e36140d.127f1c", + "type": "ui_group", + "name": "PowerMeter", + "tab": "50c487c1.27e508", + "order": 2, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "ebbb0e3f.53fbf", + "type": "ui_group", + "name": "Actions", + "tab": "50c487c1.27e508", + "order": 2, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "1709edaf.162962", + "type": "ui_group", + "name": "Debug", + "tab": "50c487c1.27e508", + "order": 3, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "8d6f402b.8f007", + "type": "ui_group", + "name": "KeepAlive", + "tab": "50c487c1.27e508", + "order": 5, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "1ebee360.265b5d", + "type": "ui_group", + "name": "PowerMeter", + "tab": "50c487c1.27e508", + "order": 6, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "d3f19d5c.593e5", + "type": "ui_group", + "name": "State", + "tab": "50c487c1.27e508", + "order": 4, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "b364f7eb4621082b", + "type": "ui_group", + "name": "Connector 1 [ISO15118]", + "tab": "d3ada9fa4cf6ac53", + "order": 2, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "7cd2ccabb1265f7a", + "type": "ui_group", + "name": "RFID", + "tab": "d3ada9fa4cf6ac53", + "order": 1, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "21e40a4a97a50168", + "type": "ui_group", + "name": "Connector 2 [Basic Charging]", + "tab": "d3ada9fa4cf6ac53", + "order": 3, + "disp": true, + "width": "6", + "collapse": false + }, + { + "id": "50c487c1.27e508", + "type": "ui_tab", + "name": "Debug", + "icon": "fa-fire", + "disabled": false, + "hidden": false + }, + { + "id": "d3ada9fa4cf6ac53", + "type": "ui_tab", + "name": "Home", + "icon": "dashboard", + "order": 1, + "disabled": false, + "hidden": false + }, + { + "id": "27225dc1005441da", + "type": "ui_spacer", + "z": "9aafbf849d4d6e12", + "name": "spacer", + "group": "27651fee38a05406", + "order": 4, + "width": 1, + "height": 1 + }, + { + "id": "7120e41583a9165f", + "type": "ui_spacer", + "z": "9aafbf849d4d6e12", + "name": "spacer", + "group": "27651fee38a05406", + "order": 4, + "width": 1, + "height": 1 + }, + { + "id": "efce370cfc8e4f9b", + "type": "ui_spacer", + "z": "9aafbf849d4d6e12", + "name": "spacer", + "group": "", + "order": 2, + "width": 6, + "height": 1 + }, + { + "id": "794d727ae2866f12", + "type": "ui_spacer", + "z": "9aafbf849d4d6e12", + "name": "spacer", + "group": "", + "order": 5, + "width": "6", + "height": "1" + }, + { + "id": "c8955752ad17f297", + "type": "mqtt in", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/powermeter/vrmsL1", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 180, + "y": 100, + "wires": [ + [ + "b1d3d31a92c2c68d" + ] + ] + }, + { + "id": "b1d3d31a92c2c68d", + "type": "ui_chart", + "z": "9aafbf849d4d6e12", + "name": "", + "group": "5e36140d.127f1c", + "order": 11, + "width": 0, + "height": 0, + "label": "vrmsL1", + "chartType": "line", + "legend": "false", + "xformat": "HH:mm:ss", + "interpolate": "linear", + "nodata": "", + "dot": false, + "ymin": "", + "ymax": "", + "removeOlder": "60", + "removeOlderPoints": "", + "removeOlderUnit": "1", + "cutout": 0, + "useOneColor": false, + "useUTC": false, + "colors": [ + "#1f77b4", + "#aec7e8", + "#ff7f0e", + "#2ca02c", + "#98df8a", + "#d62728", + "#ff9896", + "#9467bd", + "#c5b0d5" + ], + "outputs": 1, + "useDifferentColor": false, + "x": 400, + "y": 100, + "wires": [ + [] + ] + }, + { + "id": "a4bef87a56ade625", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/enable", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 500, + "y": 440, + "wires": [] + }, + { + "id": "6c1a9684ee9cff1b", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/disable", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 500, + "y": 480, + "wires": [] + }, + { + "id": "7b7910abcebe9ea8", + "type": "ui_switch", + "z": "9aafbf849d4d6e12", + "name": "", + "label": "Enabled", + "tooltip": "", + "group": "ebbb0e3f.53fbf", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "topic", + "topicType": "msg", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 120, + "y": 460, + "wires": [ + [ + "f68fa199eb0c13b0" + ] + ] + }, + { + "id": "f68fa199eb0c13b0", + "type": "switch", + "z": "9aafbf849d4d6e12", + "name": "", + "property": "payload", + "propertyType": "msg", + "rules": [ + { + "t": "true" + }, + { + "t": "false" + } + ], + "checkall": "true", + "repair": false, + "outputs": 2, + "x": 280, + "y": 460, + "wires": [ + [ + "a4bef87a56ade625" + ], + [ + "6c1a9684ee9cff1b" + ] + ] + }, + { + "id": "be7373d5f1fc78e3", + "type": "ui_switch", + "z": "9aafbf849d4d6e12", + "name": "", + "label": "setThreePhases", + "tooltip": "", + "group": "ebbb0e3f.53fbf", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "topic", + "topicType": "msg", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 140, + "y": 540, + "wires": [ + [ + "ee61573475970e13" + ] + ] + }, + { + "id": "ee61573475970e13", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/set_three_phases", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 370, + "y": 540, + "wires": [] + }, + { + "id": "1e33ee217d09343a", + "type": "ui_switch", + "z": "9aafbf849d4d6e12", + "name": "", + "label": "enableRCD", + "tooltip": "", + "group": "ebbb0e3f.53fbf", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "topic", + "topicType": "msg", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 130, + "y": 600, + "wires": [ + [ + "7f1db77313661cf3" + ] + ] + }, + { + "id": "7f1db77313661cf3", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/enable_rcd", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 350, + "y": 600, + "wires": [] + }, + { + "id": "23610d2f3c1a674b", + "type": "ui_switch", + "z": "9aafbf849d4d6e12", + "name": "", + "label": "setHasVentilation", + "tooltip": "", + "group": "ebbb0e3f.53fbf", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "topic", + "topicType": "msg", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 150, + "y": 660, + "wires": [ + [ + "d40cd5658151e3ca" + ] + ] + }, + { + "id": "d40cd5658151e3ca", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/set_has_ventilation", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 420, + "y": 660, + "wires": [] + }, + { + "id": "d62a89349e2d9147", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/set_auth", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 380, + "y": 220, + "wires": [] + }, + { + "id": "49a61fca4e975f0d", + "type": "ui_button", + "z": "9aafbf849d4d6e12", + "name": "", + "group": "ebbb0e3f.53fbf", + "order": 1, + "width": 0, + "height": 0, + "passthru": false, + "label": "setAuth(USERID)", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "USERID", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 150, + "y": 220, + "wires": [ + [ + "d62a89349e2d9147" + ] + ] + }, + { + "id": "4a20ae416f941363", + "type": "ui_switch", + "z": "9aafbf849d4d6e12", + "name": "", + "label": "switch3phWhileCharging", + "tooltip": "", + "group": "ebbb0e3f.53fbf", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "topic", + "topicType": "msg", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 170, + "y": 720, + "wires": [ + [ + "9f997a83d8c5e502" + ] + ] + }, + { + "id": "9f997a83d8c5e502", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/switch_three_phases_while_charging", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 530, + "y": 720, + "wires": [] + }, + { + "id": "b0dbe5826f92035e", + "type": "mqtt in", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/debug_json", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 160, + "y": 960, + "wires": [ + [ + "a62346afa82d2aa1" + ] + ] + }, + { + "id": "a62346afa82d2aa1", + "type": "json", + "z": "9aafbf849d4d6e12", + "name": "", + "property": "payload", + "action": "", + "pretty": false, + "x": 350, + "y": 960, + "wires": [ + [ + "971b16b8195f14bb" + ] + ] + }, + { + "id": "971b16b8195f14bb", + "type": "function", + "z": "9aafbf849d4d6e12", + "name": "", + "func": "var json = msg.payload;\n\n\n//tab[top] = {'Variable' : top, 'Value' : pay};\n\n\nnewpayload = [];\nfor(var index in json) {\n newpayload.push({'Variable' : index, 'Value' : json[index]});\n}\n\nmsg.payload = newpayload;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 510, + "y": 960, + "wires": [ + [ + "b79f8c3549cdb63e" + ] + ] + }, + { + "id": "b79f8c3549cdb63e", + "type": "ui_table", + "z": "9aafbf849d4d6e12", + "group": "1709edaf.162962", + "name": "Debug", + "order": 12, + "width": "6", + "height": "11", + "columns": [], + "outputs": 0, + "cts": false, + "x": 680, + "y": 960, + "wires": [] + }, + { + "id": "105b9eab50b4db7f", + "type": "mqtt in", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/keepalive_json", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 170, + "y": 1140, + "wires": [ + [ + "f9bce2148fd0d745" + ] + ] + }, + { + "id": "f9bce2148fd0d745", + "type": "json", + "z": "9aafbf849d4d6e12", + "name": "", + "property": "payload", + "action": "", + "pretty": false, + "x": 350, + "y": 1140, + "wires": [ + [ + "81c08d60fe305390" + ] + ] + }, + { + "id": "81c08d60fe305390", + "type": "function", + "z": "9aafbf849d4d6e12", + "name": "", + "func": "var json = msg.payload;\n\n\n//tab[top] = {'Variable' : top, 'Value' : pay};\n\n\nnewpayload = [];\nfor(var index in json) {\n newpayload.push({'Variable' : index, 'Value' : json[index]});\n}\n\nmsg.payload = newpayload;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 510, + "y": 1140, + "wires": [ + [ + "24770a798c2cc78c" + ] + ] + }, + { + "id": "24770a798c2cc78c", + "type": "ui_table", + "z": "9aafbf849d4d6e12", + "group": "8d6f402b.8f007", + "name": "KeepAlive", + "order": 12, + "width": "6", + "height": "4", + "columns": [], + "outputs": 0, + "cts": false, + "x": 690, + "y": 1140, + "wires": [] + }, + { + "id": "b2e6e05e396ff846", + "type": "mqtt in", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/powermeter_json", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 170, + "y": 1200, + "wires": [ + [ + "bff81951aab38e7c" + ] + ] + }, + { + "id": "bff81951aab38e7c", + "type": "json", + "z": "9aafbf849d4d6e12", + "name": "", + "property": "payload", + "action": "", + "pretty": false, + "x": 360, + "y": 1200, + "wires": [ + [ + "685dcdcf457910a6" + ] + ] + }, + { + "id": "685dcdcf457910a6", + "type": "function", + "z": "9aafbf849d4d6e12", + "name": "", + "func": "var json = msg.payload;\n\n\n//tab[top] = {'Variable' : top, 'Value' : pay};\n\n\nnewpayload = [];\nfor(var index in json) {\n newpayload.push({'Variable' : index, 'Value' : json[index]});\n}\n\nmsg.payload = newpayload;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 520, + "y": 1200, + "wires": [ + [ + "854cee03b9be0de5" + ] + ] + }, + { + "id": "854cee03b9be0de5", + "type": "ui_table", + "z": "9aafbf849d4d6e12", + "group": "1ebee360.265b5d", + "name": "PowerMeter", + "order": 12, + "width": "6", + "height": "11", + "columns": [], + "outputs": 0, + "cts": false, + "x": 710, + "y": 1200, + "wires": [] + }, + { + "id": "dd9a01731c23f076", + "type": "mqtt in", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/state/#", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 140, + "y": 1020, + "wires": [ + [ + "181994551d5096a8" + ] + ] + }, + { + "id": "181994551d5096a8", + "type": "function", + "z": "9aafbf849d4d6e12", + "name": "", + "func": "let cur_topic_index = -1;\nlet topics_list_length = 0;\n\nvar topics_list = global.get(\"state_topics_list\");\nvar payload_list = global.get(\"state_payload_list\");\nvar new_payload = [];\n\nfor (var topics_list_index in topics_list) {\n if ( (topics_list[topics_list_index].indexOf(msg.topic) >= 0) && (topics_list[topics_list_index].length == msg.topic.length) ) {\n cur_topic_index = topics_list_index;\n break;\n }\n topics_list_length++;\n}\n\nif (cur_topic_index > -1) {\n payload_list[cur_topic_index] = msg.payload;\n} else {\n topics_list.push(msg.topic);\n payload_list.push(msg.payload);\n}\n\nglobal.set(\"state_topics_list\", topics_list);\nglobal.set(\"state_payload_list\", payload_list);\n\n\nfor (var index in payload_list) {\n new_payload.push({'Variable': topics_list[index].substr(16, topics_list[index].length), 'Value': payload_list[index]});\n}\n\nmsg.payload = new_payload;\nreturn msg;\n", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nglobal.set(\"state_topics_list\", []);\nglobal.set(\"state_payload_list\", []);", + "finalize": "", + "libs": [], + "x": 520, + "y": 1020, + "wires": [ + [ + "f05a2bcbad4e5e4f" + ] + ] + }, + { + "id": "f05a2bcbad4e5e4f", + "type": "ui_table", + "z": "9aafbf849d4d6e12", + "group": "d3f19d5c.593e5", + "name": "State", + "order": 12, + "width": "6", + "height": "4", + "columns": [], + "outputs": 0, + "cts": false, + "x": 690, + "y": 1020, + "wires": [] + }, + { + "id": "626ae76afca27c10", + "type": "ui_switch", + "z": "9aafbf849d4d6e12", + "name": "", + "label": "enableHLC", + "tooltip": "", + "group": "ebbb0e3f.53fbf", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "topic", + "topicType": "msg", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 130, + "y": 780, + "wires": [ + [ + "cb19212395df1ec4" + ] + ] + }, + { + "id": "cb19212395df1ec4", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/enable_hlc", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 350, + "y": 780, + "wires": [] + }, + { + "id": "8761f22ed645e3d5", + "type": "ui_switch", + "z": "9aafbf849d4d6e12", + "name": "", + "label": "Simulation RCD", + "tooltip": "", + "group": "ebbb0e3f.53fbf", + "order": 5, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "topic", + "topicType": "msg", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 140, + "y": 840, + "wires": [ + [ + "c423c5096c47f04a" + ] + ] + }, + { + "id": "c423c5096c47f04a", + "type": "mqtt out", + "z": "9aafbf849d4d6e12", + "name": "", + "topic": "/external/cmd/enable_rcd", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 350, + "y": 840, + "wires": [] + }, + { + "id": "edcc986828bdfcc4", + "type": "comment", + "z": "9aafbf849d4d6e12", + "name": "Debug", + "info": "", + "x": 110, + "y": 40, + "wires": [] + }, + { + "id": "957c91b46df24e3a", + "type": "mqtt out", + "z": "e921db8897354328", + "name": "", + "topic": "everest_api/dummy_token_provider/cmd/provide", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 1000, + "y": 120, + "wires": [] + }, + { + "id": "765ecd7720d3a54e", + "type": "ui_dropdown", + "z": "e921db8897354328", + "name": "", + "label": "id_token", + "tooltip": "", + "place": "Select option", + "group": "7cd2ccabb1265f7a", + "order": 1, + "width": 0, + "height": 0, + "passthru": true, + "multiple": false, + "options": [ + { + "label": "DEADBEEF", + "value": "DEADBEEF", + "type": "str" + }, + { + "label": "ABC12345", + "value": "ABC12345", + "type": "str" + }, + { + "label": "VID:AABBCCDDEEFF", + "value": "VID:AABBCCDDEEFF", + "type": "str" + } + ], + "payload": "", + "topic": "id_token", + "topicType": "str", + "x": 410, + "y": 100, + "wires": [ + [ + "061c0a7744e15ba2" + ] + ] + }, + { + "id": "352aa4429de054a6", + "type": "ui_switch", + "z": "e921db8897354328", + "name": "", + "label": "prevalidated", + "tooltip": "", + "group": "7cd2ccabb1265f7a", + "order": 3, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "prevalidated", + "topicType": "str", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 410, + "y": 220, + "wires": [ + [ + "061c0a7744e15ba2" + ] + ] + }, + { + "id": "a950f5bfc61638ee", + "type": "ui_text_input", + "z": "e921db8897354328", + "name": "", + "label": "type (2-32)", + "tooltip": "", + "group": "7cd2ccabb1265f7a", + "order": 2, + "width": 0, + "height": 0, + "passthru": true, + "mode": "text", + "delay": 300, + "topic": "token_type", + "topicType": "str", + "x": 410, + "y": 160, + "wires": [ + [ + "061c0a7744e15ba2" + ] + ] + }, + { + "id": "be6b6aec975d1d27", + "type": "debug", + "z": "e921db8897354328", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 890, + "y": 200, + "wires": [] + }, + { + "id": "054b87b685588c2c", + "type": "inject", + "z": "e921db8897354328", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "1", + "topic": "id_token", + "payload": "DEADBEEF", + "payloadType": "str", + "x": 180, + "y": 100, + "wires": [ + [ + "765ecd7720d3a54e" + ] + ] + }, + { + "id": "045f2c88f1a7077e", + "type": "inject", + "z": "e921db8897354328", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "1", + "topic": "token_type", + "payload": "RFID", + "payloadType": "str", + "x": 200, + "y": 160, + "wires": [ + [ + "a950f5bfc61638ee" + ] + ] + }, + { + "id": "b3b44ea9f00b7d45", + "type": "inject", + "z": "e921db8897354328", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "1", + "topic": "prevalidated", + "payload": "false", + "payloadType": "bool", + "x": 170, + "y": 220, + "wires": [ + [ + "352aa4429de054a6" + ] + ] + }, + { + "id": "061c0a7744e15ba2", + "type": "function", + "z": "e921db8897354328", + "name": "Swipe token", + "func": "if (msg.topic.indexOf('id_token') > -1) flow.set('id_token', msg.payload);\nif (msg.topic.indexOf('token_type') > -1) flow.set('token_type', msg.payload);\nif (msg.topic.indexOf('prevalidated') > -1) flow.set('prevalidated', msg.payload);\nif (msg.topic.indexOf('connectors') > -1) flow.set('connectors', msg.payload);\nif (msg.topic.indexOf('complete') > -1) {\n msg.payload = {\n 'id_token': {\n 'value': flow.get('id_token'),\n 'type': 'ISO14443'\n},\n 'authorization_type': flow.get('token_type'),\n 'prevalidated': flow.get('prevalidated'),\n 'connectors': [flow.get('connectors')]\n };\n return msg;\n}\n", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 690, + "y": 160, + "wires": [ + [ + "be6b6aec975d1d27", + "957c91b46df24e3a" + ] + ] + }, + { + "id": "6cc9edbf2f3b9aa6", + "type": "ui_button", + "z": "e921db8897354328", + "name": "", + "group": "7cd2ccabb1265f7a", + "order": 4, + "width": 0, + "height": 0, + "passthru": false, + "label": "Swipe RFID", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "", + "payloadType": "str", + "topic": "complete", + "topicType": "str", + "x": 410, + "y": 380, + "wires": [ + [ + "061c0a7744e15ba2" + ] + ] + }, + { + "id": "15f66c1947e2cb18", + "type": "inject", + "z": "e921db8897354328", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "1", + "topic": "connectors", + "payload": "1", + "payloadType": "num", + "x": 150, + "y": 300, + "wires": [ + [ + "deedf2d418fbc8fd" + ] + ] + }, + { + "id": "deedf2d418fbc8fd", + "type": "ui_numeric", + "z": "e921db8897354328", + "name": "", + "label": "connectors", + "tooltip": "", + "group": "7cd2ccabb1265f7a", + "order": 0, + "width": "0", + "height": "0", + "wrap": false, + "passthru": true, + "topic": "connectors", + "topicType": "msg", + "format": "{{value}}", + "min": "1", + "max": 10, + "step": 1, + "x": 410, + "y": 300, + "wires": [ + [ + "061c0a7744e15ba2" + ] + ] + }, + { + "id": "e8e1511b6239bfa2", + "type": "comment", + "z": "ed603c51db9dcbb9", + "name": "Initialize the Connector number", + "info": "", + "x": 230, + "y": 80, + "wires": [] + }, + { + "id": "23d875eef4c57fa8", + "type": "change", + "z": "ed603c51db9dcbb9", + "name": "", + "rules": [ + { + "t": "set", + "p": "connector_number", + "pt": "flow", + "to": "1", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 440, + "y": 140, + "wires": [ + [] + ] + }, + { + "id": "e99161497760c072", + "type": "inject", + "z": "ed603c51db9dcbb9", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": 0.1, + "topic": "", + "payloadType": "date", + "x": 190, + "y": 140, + "wires": [ + [ + "23d875eef4c57fa8" + ] + ] + }, + { + "id": "b70c30908c955b81", + "type": "comment", + "z": "ed603c51db9dcbb9", + "name": "Data to show", + "info": "", + "x": 170, + "y": 200, + "wires": [] + }, + { + "id": "f96ccb60614f9f18", + "type": "mqtt out", + "z": "ed603c51db9dcbb9", + "name": "", + "topic": "", + "qos": "1", + "retain": "false", + "respTopic": "", + "contentType": "", + "userProps": "", + "correl": "", + "expiry": "", + "broker": "fc8686af.48d178", + "x": 890, + "y": 840, + "wires": [] + }, + { + "id": "3a5423dc1feed224", + "type": "ui_button", + "z": "ed603c51db9dcbb9", + "name": "", + "group": "b364f7eb4621082b", + "order": 1, + "width": "3", + "height": "1", + "passthru": false, + "label": "Pause", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "", + "payloadType": "str", + "topic": "everest_external/nodered/#/cmd/pause_charging", + "topicType": "str", + "x": 150, + "y": 700, + "wires": [ + [ + "361b3d846c4e6673" + ] + ] + }, + { + "id": "f042bc45e3742ef7", + "type": "ui_button", + "z": "ed603c51db9dcbb9", + "name": "", + "group": "b364f7eb4621082b", + "order": 2, + "width": "3", + "height": "1", + "passthru": false, + "label": "Resume", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "", + "payloadType": "str", + "topic": "everest_external/nodered/#/cmd/resume_charging", + "topicType": "str", + "x": 160, + "y": 760, + "wires": [ + [ + "361b3d846c4e6673" + ] + ] + }, + { + "id": "361b3d846c4e6673", + "type": "change", + "z": "ed603c51db9dcbb9", + "name": "Insert Connector number", + "rules": [ + { + "t": "change", + "p": "topic", + "pt": "msg", + "from": "#", + "fromt": "str", + "to": "connector_number", + "tot": "flow" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 670, + "y": 840, + "wires": [ + [ + "f96ccb60614f9f18" + ] + ] + }, + { + "id": "9c6d1a5e2ba43d36", + "type": "comment", + "z": "ed603c51db9dcbb9", + "name": "Commands", + "info": "", + "x": 170, + "y": 640, + "wires": [] + }, + { + "id": "51656271f4688a67", + "type": "mqtt in", + "z": "ed603c51db9dcbb9", + "name": "", + "topic": "everest_external/nodered/+/state/max_current", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 270, + "y": 280, + "wires": [ + [ + "f5ca89d3e6d1d1ba" + ] + ] + }, + { + "id": "f5ca89d3e6d1d1ba", + "type": "function", + "z": "ed603c51db9dcbb9", + "name": "Filter connector number", + "func": "if (msg.topic.indexOf(String(flow.get('connector_number'))) > -1) return msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 590, + "y": 280, + "wires": [ + [ + "1a019e35e580cbf4" + ] + ] + }, + { + "id": "1a019e35e580cbf4", + "type": "ui_text", + "z": "ed603c51db9dcbb9", + "group": "b364f7eb4621082b", + "order": 3, + "width": 0, + "height": 0, + "name": "", + "label": "Max Current", + "format": "{{msg.payload | number: 1}}", + "layout": "row-spread", + "x": 890, + "y": 280, + "wires": [] + }, + { + "id": "799130c039278e9b", + "type": "ui_text", + "z": "ed603c51db9dcbb9", + "group": "b364f7eb4621082b", + "order": 5, + "width": 0, + "height": 0, + "name": "", + "label": "Energy Charged", + "format": "{{msg.payload | number:2}} kWh", + "layout": "row-spread", + "x": 880, + "y": 340, + "wires": [] + }, + { + "id": "a24c1a5f8bb6a5b5", + "type": "function", + "z": "ed603c51db9dcbb9", + "name": "Filter connector number", + "func": "if (msg.topic.indexOf(String(flow.get('connector_number'))) > -1) return msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 650, + "y": 340, + "wires": [ + [ + "799130c039278e9b" + ] + ] + }, + { + "id": "7164db07c0a78327", + "type": "mqtt in", + "z": "ed603c51db9dcbb9", + "name": "", + "topic": "everest_external/nodered/+/powermeter/totalKWattHr", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 300, + "y": 340, + "wires": [ + [ + "a24c1a5f8bb6a5b5" + ] + ] + }, + { + "id": "9715c97a5a212e0f", + "type": "mqtt in", + "z": "ed603c51db9dcbb9", + "name": "", + "topic": "everest_external/nodered/+/state/state_string", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 270, + "y": 440, + "wires": [ + [ + "0139c0ace0e5f706" + ] + ] + }, + { + "id": "8059bceefd9dcdf7", + "type": "mqtt in", + "z": "ed603c51db9dcbb9", + "name": "", + "topic": "everest_external/nodered/+/powermeter/totalKw", + "qos": "2", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 280, + "y": 500, + "wires": [ + [ + "6941efd3ef8e1ac4" + ] + ] + }, + { + "id": "72a90b471d6ebb44", + "type": "ui_level", + "z": "ed603c51db9dcbb9", + "group": "b364f7eb4621082b", + "order": 7, + "width": 0, + "height": 0, + "name": "", + "label": "Temperature:", + "colorHi": "#e60000", + "colorWarn": "#ff9900", + "colorNormal": "#00b33c", + "colorOff": "#595959", + "min": "-20", + "max": "85", + "segWarn": "65", + "segHigh": "75", + "unit": "", + "layout": "sh", + "channelA": "", + "channelB": "", + "decimals": 0, + "animations": "soft", + "shape": "3", + "colorschema": "valuedriven", + "textoptions": "default", + "colorText": "#eeeeee", + "fontLabel": "", + "fontValue": "", + "fontSmall": "", + "colorFromTheme": true, + "textAnimations": false, + "hideValue": false, + "tickmode": "segments", + "peakmode": false, + "property": "payload", + "peaktime": 3000, + "x": 1010, + "y": 560, + "wires": [] + }, + { + "id": "4b864ea8df7d27cf", + "type": "mqtt in", + "z": "ed603c51db9dcbb9", + "name": "", + "topic": "everest_external/nodered/+/state/temperature", + "qos": "0", + "datatype": "auto", + "broker": "fc8686af.48d178", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 280, + "y": 560, + "wires": [ + [ + "bc1b6adb5db9d7e4" + ] + ] + }, + { + "id": "1ef49a48bf883748", + "type": "function", + "z": "ed603c51db9dcbb9", + "name": "", + "func": "if (msg.topic.indexOf('totalKw')>=0) {\n console.warn(\"Received totalKw message\");\n if (context.data.stop_updating_kw) {\n console.error(\"stopping kw updates because this is annoying\")\n } else {\n context.data.totalKw = msg.payload;\n }\n}\nelse if (msg.topic.indexOf('state_string')>=0) {\n console.error(\"how can I show the logs\");\n context.data.state_string = msg.payload;\n if (msg.payload === 'Charging') {\n context.data.totalKw = 200;\n context.data.stop_updating_kw = true;\n } else {\n context.data.stop_updating_kw = false;\n }\n}\n\n//node.warn(msg.topic);\nmsg.payload = context.data.totalKw;\nmsg.label = context.data.state_string;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\ncontext.data = {}", + "finalize": "", + "libs": [], + "x": 980, + "y": 480, + "wires": [ + [ + "5bd8abc274e70360", + "1295e032d7ddbc20" + ] + ] + }, + { + "id": "5bd8abc274e70360", + "type": "debug", + "z": "ed603c51db9dcbb9", + "name": "", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 1190, + "y": 380, + "wires": [] + }, + { + "id": "1295e032d7ddbc20", + "type": "ui_gauge", + "z": "ed603c51db9dcbb9", + "g": "af1e1eeac9c4b704", + "name": "", + "group": "b364f7eb4621082b", + "order": 6, + "width": 0, + "height": 0, + "gtype": "gage", + "title": "{{msg.label}}", + "label": "Kilowatt", + "format": "{{value}} kW", + "min": "0", + "max": "11", + "colors": [ + "#00b500", + "#e6e600", + "#ca3838" + ], + "seg1": "", + "seg2": "", + "x": 1190, + "y": 480, + "wires": [] + }, + { + "id": "6941efd3ef8e1ac4", + "type": "function", + "z": "ed603c51db9dcbb9", + "name": "Filter connector number", + "func": "if (msg.topic.indexOf(String(flow.get('connector_number'))) > -1) return msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 730, + "y": 500, + "wires": [ + [ + "1ef49a48bf883748" + ] + ] + }, + { + "id": "0139c0ace0e5f706", + "type": "function", + "z": "ed603c51db9dcbb9", + "name": "Filter connector number", + "func": "if (msg.topic.indexOf(String(flow.get('connector_number'))) > -1) return msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 730, + "y": 460, + "wires": [ + [ + "1ef49a48bf883748" + ] + ] + }, + { + "id": "bc1b6adb5db9d7e4", + "type": "function", + "z": "ed603c51db9dcbb9", + "name": "Filter connector number", + "func": "if (msg.topic.indexOf(String(flow.get('connector_number'))) > -1) return msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 710, + "y": 560, + "wires": [ + [ + "72a90b471d6ebb44" + ] + ] + }, + { + "id": "348dfdc4b48ec881", + "type": "comment", + "z": "ed603c51db9dcbb9", + "name": "Simulation control", + "info": "", + "x": 190, + "y": 820, + "wires": [] + }, + { + "id": "352d9f34ae594f58", + "type": "ui_switch", + "z": "ed603c51db9dcbb9", + "name": "", + "label": "Simulation enable (HIL)", + "tooltip": "", + "group": "b364f7eb4621082b", + "order": 10, + "width": 0, + "height": 0, + "passthru": true, + "decouple": "false", + "topic": "everest_external/nodered/carsim/#/cmd/enable", + "topicType": "str", + "style": "", + "onvalue": "true", + "onvalueType": "bool", + "onicon": "", + "oncolor": "", + "offvalue": "false", + "offvalueType": "bool", + "officon": "", + "offcolor": "", + "animate": false, + "x": 210, + "y": 880, + "wires": [ + [ + "361b3d846c4e6673" + ] + ] + }, + { + "id": "76be7e55a947f675", + "type": "ui_button", + "z": "ed603c51db9dcbb9", + "name": "", + "group": "b364f7eb4621082b", + "order": 8, + "width": "3", + "height": "1", + "passthru": false, + "label": "Car Plugin", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "start", + "payloadType": "str", + "topic": "everest_external/nodered/#/carsim/cmd/execute_charging_session", + "topicType": "str", + "x": 170, + "y": 980, + "wires": [ + [ + "620b0d248a89ece0" + ] + ] + }, + { + "id": "42f4c4f916474559", + "type": "ui_button", + "z": "ed603c51db9dcbb9", + "name": "", + "group": "b364f7eb4621082b", + "order": 9, + "width": "3", + "height": "1", + "passthru": false, + "label": "Stop & Unplug", + "tooltip": "", + "color": "", + "bgcolor": "", + "icon": "", + "payload": "stop", + "payloadType": "str", + "topic": "everest_external/nodered/#/carsim/cmd/modify_charging_session", + "topicType": "str", + "x": 180, + "y": 940, + "wires": [ + [ + "620b0d248a89ece0" + ] + ] + }, + { + "id": "cc45f1b73782292a", + "type": "ui_slider", + "z": "ed603c51db9dcbb9", + "name": "MaxCurrent Slider", + "label": "", + "tooltip": "", + "group": "b364f7eb4621082b", + "order": 4, + "width": 0, + "height": 0, + "passthru": false, + "outs": "all", + "topic": "everest_external/nodered/#/cmd/set_max_current", + "topicType": "str", + "min": "6", + "max": "32", + "step": "0.1", + "x": 450, + "y": 700, + "wires": [ + [ + "361b3d846c4e6673" + ] + ] + }, + { + "id": "f2ae0c306f3052f9", + "type": "ui_dropdown", + "z": "ed603c51db9dcbb9", + "name": "", + "label": "Car Simulation", + "tooltip": "", + "place": "Select option", + "group": "b364f7eb4621082b", + "order": 10, + "width": 0, + "height": 0, + "passthru": true, + "multiple": false, + "options": [ + { + "label": "AC ISO15118-2", + "value": "sleep 1;iso_wait_slac_matched;iso_start_v2g_session ExternalPayment,AC_three_phase_core;iso_wait_pwr_ready;iso_draw_power_regulated 16,3;sleep 36000#iso_stop_charging;iso_wait_v2g_session_stopped;unplug#iso_pause_charging;iso_wait_for_resume#iso_start_bcb_toogle 3;iso_wait_pwm_is_running;iso_start_v2g_session ExternalPayment,AC_three_phase_core;iso_wait_pwr_ready;iso_draw_power_regulated 16,3;sleep 36000", + "type": "str" + }, + { + "label": "AC ISO15118-2 Plug&Charge", + "value": "sleep 1;iso_wait_slac_matched;iso_start_v2g_session contract,AC_three_phase_core;iso_wait_pwr_ready;iso_draw_power_regulated 16,3;sleep 36000#;iso_stop_charging;iso_wait_v2g_session_stopped;unplug#iso_pause_charging;iso_wait_for_resume#iso_start_bcb_toogle 3;iso_wait_pwm_is_running;iso_start_v2g_session contract,AC_three_phase_core;iso_wait_pwr_ready;iso_draw_power_regulated 16,3;sleep 36000", + "type": "str" + } + ], + "payload": "", + "topic": "sim_commands", + "topicType": "str", + "x": 180, + "y": 1120, + "wires": [ + [ + "620b0d248a89ece0" + ] + ] + }, + { + "id": "620b0d248a89ece0", + "type": "function", + "z": "ed603c51db9dcbb9", + "name": "Buffer sim commands", + "func": "if (msg.topic.indexOf('sim_commands') > -1) {\n const s = msg.payload.split('#');\n flow.set('sim_commands_start', s[0]);\n flow.set('sim_commands_stop', s[1]);\n flow.set('sim_commands_pause', s[2]);\n flow.set('sim_commands_resume', s[3]);\n} else if (msg.payload == 'start') {\n msg.payload = flow.get('sim_commands_start');\n return msg;\n} else if (msg.payload == 'stop') {\n msg.payload = flow.get('sim_commands_stop');\n return msg;\n} else if (msg.payload == 'pause') {\n msg.payload = flow.get('sim_commands_pause');\n return msg;\n} else if (msg.payload == 'resume') {\n msg.payload = flow.get('sim_commands_resume');\n return msg;\n} else {\n msg.payload = 'NONE';\n return msg;\n}\n", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 440, + "y": 960, + "wires": [ + [ + "361b3d846c4e6673", + "cc42c210398a8d50", + "fb1511183c9a660f" + ] + ] + }, + { + "id": "fb1511183c9a660f", + "type": "debug", + "z": "ed603c51db9dcbb9", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 910, + "y": 920, + "wires": [] + }, + { + "id": "fef2be4575e66bda", + "type": "inject", + "z": "ed603c51db9dcbb9", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "1", + "topic": "sim_commands", + "payload": "sleep 1;iec_wait_pwr_ready;sleep 1;draw_power_regulated 16,3;sleep 36000#unplug#pause;sleep 3600#draw_power_regulated 16,3;sleep 36000", + "payloadType": "str", + "x": 150, + "y": 1180, + "wires": [ + [ + "f2ae0c306f3052f9" + ] + ] + }, + { + "id": "cc42c210398a8d50", + "type": "debug", + "z": "ed603c51db9dcbb9", + "name": "", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "topic", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 900, + "y": 960, + "wires": [] + }, + { + "id": "29aced2052ea8795", + "type": "ui_button", + "z": "ed603c51db9dcbb9", + "name": "", + "group": "b364f7eb4621082b", + "order": 9, + "width": "3", + "height": "1", + "passthru": false, + "label": "EV Pause", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "pause", + "payloadType": "str", + "topic": "everest_external/nodered/#/carsim/cmd/modify_charging_session", + "topicType": "str", + "x": 160, + "y": 1020, + "wires": [ + [ + "620b0d248a89ece0" + ] + ] + }, + { + "id": "f39e2131a37761a6", + "type": "ui_button", + "z": "ed603c51db9dcbb9", + "name": "", + "group": "b364f7eb4621082b", + "order": 9, + "width": "3", + "height": "1", + "passthru": false, + "label": "EV Resume", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "resume", + "payloadType": "str", + "topic": "everest_external/nodered/#/carsim/cmd/modify_charging_session", + "topicType": "str", + "x": 170, + "y": 1060, + "wires": [ + [ + "620b0d248a89ece0" + ] + ] + } +] From 451e4b2d4b0476bb97508520577b6f8bf2409aa5 Mon Sep 17 00:00:00 2001 From: the-bay-kay Date: Wed, 12 Jun 2024 17:51:12 -0700 Subject: [PATCH 9/9] Fixed README Formatting, Added Eonti Certs - Fixed README Format Issues - Added `-e` flag to use EonTi certs with Security Profile 2 or 3 Signed-off-by: the-bay-kay --- README.md | 6 +- demo-iso15118-2-ac-plus-ocpp.sh | 53 ++++++++++++------ .../cached_certs_correct_name_emaid.tar.gz | Bin 26153 -> 28824 bytes 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index cada660a..37e6256b 100644 --- a/README.md +++ b/README.md @@ -100,10 +100,10 @@ in your terminal before one of the one-liners presented in the next section. - Access the visual representation at http://localhost:8849 ### OPTIONAL: Explore a demo with Edgeshark -- For each demo of ISO 15118-2 AC Charging with OCPP, the demo may be ran alongside [Edgeshark](httpos://github.com/siemans/edgeshark?tab=readme-ov-file#edgeshark) for additional information on the network connection between each container. -- To launch a Edgeshark alongside one of these demos, include the -s flag when running the demo +- For each demo using `demo-iso15118-2-ac-plus-ocpp.sh`, the demo may be ran alongside [Edgeshark](httpos://github.com/siemans/edgeshark?tab=readme-ov-file#edgeshark) for additional information on the network connection between each container. +- To launch a Edgeshark alongside one of these demos, include the `-s` flag when running the demo - 🦈 Example Profile 2 ⚡: `curl https://raw.githubusercontent.com/everest/everest-demo/main/demo-iso15118-2-ac-plus-ocpp.sh | bash -s -- -s -2` -- Using your preferred web browser, navigate to http://localhost:5001 and explore the connections via the discovery engine. +- Using your preferred web browser, navigate to [http://localhost:5001](http://localhost:5001) and explore the connections via the discovery engine. - If you have installed the [cshargextcap plugin](https://github.com/siemens/cshargextcap?tab=readme-ov-file#containershark-extcap-plugin-for-wireshark), you may launch [Wireshark Desktop](https://www.wireshark.org/download.html) from the discovery engine, to perform a live capture. ### TEARDOWN: Clean up after the demo diff --git a/demo-iso15118-2-ac-plus-ocpp.sh b/demo-iso15118-2-ac-plus-ocpp.sh index 980c54a8..17dad9a4 100755 --- a/demo-iso15118-2-ac-plus-ocpp.sh +++ b/demo-iso15118-2-ac-plus-ocpp.sh @@ -11,7 +11,7 @@ CSMS="maeve" -usage="usage: $(basename "$0") [-r ] [-b ] [-c ] [-s] [-j|1|2|3] [-h] +usage="usage: $(basename "$0") [-r ] [-b ] [-c ] [-s] [-e] [-j|1|2|3] [-h] This script will run EVerest ISO 15118-2 AC charging with OCPP demos. @@ -23,6 +23,7 @@ where: -b Branch of everest-demo repo to use (default: $DEMO_BRANCH) -c Use CitrineOS CSMS (default: MaEVe) -s Run with Edgeshark + -e Use EonTi Certificates (with OCPP v2.0.1 Security Profile 2 or 3) -j OCPP v1.6j -1 OCPP v2.0.1 Security Profile 1 -2 OCPP v2.0.1 Security Profile 2 @@ -33,9 +34,10 @@ where: DEMO_VERSION= DEMO_COMPOSE_FILE_NAME= RUN_WITH_EDGESHARK=false +CERTS="self_signed" # loop through positional options/arguments -while getopts ':r:b:c:sj123h' option; do +while getopts ':r:b:c:sej123h' option; do case "$option" in r) DEMO_REPO="$OPTARG" ;; b) DEMO_BRANCH="$OPTARG" ;; @@ -43,6 +45,7 @@ while getopts ':r:b:c:sj123h' option; do CSMS_REPO="https://github.com/citrineos/citrineos-core" CSMS_BRANCH="63670f3adc09266a0977862d972b0f7e440c577f" ;; s) RUN_WITH_EDGESHARK=true ;; + e) CERTS="eonti" ;; j) DEMO_VERSION="v1.6j" DEMO_COMPOSE_FILE_NAME="docker-compose.ocpp16j.yml" ;; 1) DEMO_VERSION="v2.0.1-sp1" @@ -137,7 +140,21 @@ if [[ "$DEMO_VERSION" != v1.6j ]]; then if [[ "$CSMS" == "citrine" ]]; then echo "Security profile 2/3 is not supported with Citrine yet!" exit 1 - else + fi + if [[ "$CERTS" == "eonti" ]]; then + echo "Copying EonTi certs into ${DEMO_DIR}/maeve-csms/config/certificates" + tar xf cached_certs_correct_name_emaid.tar.gz + cat dist/etc/everest/certs/eonti/evse_leaf_primary \ + dist/etc/everest/certs/eonti/cpo_p256_CA2 \ + dist/etc/everest/certs/eonti/cpo_CA1 \ + > config/certificates/csms.pem + cat dist/etc/everest/certs/eonti/cpo_p256_CA2 \ + dist/etc/everest/certs/eonti/cpo_CA1 \ + > config/certificates/trust.pem + cp dist/etc/everest/certs/eonti/valid_evse_key config/certificates/csms.key + cp dist/etc/everest/certs/eonti/V2G_root config/certificates/root-V2G-cert.pem + cp dist/etc/everest/certs/eonti/MO_root config/certificates/root-MO-cert.pem + else # Use self-signed certs 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 \ @@ -150,23 +167,23 @@ if [[ "$DEMO_VERSION" != v1.6j ]]; then 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 + fi - 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 + 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 "Patching the CSMS to disable WSS" patch -p1 -i ../everest-demo/maeve/maeve-csms-no-wss.patch diff --git a/manager/cached_certs_correct_name_emaid.tar.gz b/manager/cached_certs_correct_name_emaid.tar.gz index 3e06f1682d8ebf28f80086f5a74db434f187aaa3..99ff6511614179ddc1b03338503dc97de64187c6 100644 GIT binary patch literal 28824 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

tW1-VRH1xWFeu=;kuDV2XtdEba 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�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