Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,108 @@
active_modules:
iso15118_car:
module: JsCarV2G
config_implementation:
main:
stack_implementation: RISE-V2G
mqtt_base_path: everest_external/iso15118/ev
device: auto
connector_1:
module: EvseManager
config_module:
connector_id: 1
three_phases: true
has_ventilation: true
country_code: DE
rcd_enabled: true
evse_id: '1'
connections:
bsp:
- module_id: yeti_driver
implementation_id: board_support
powermeter_grid_side:
- module_id: yeti_driver
implementation_id: powermeter
yeti_driver:
module: JsYetiSimulator
slac:
module: JsSlacSimulator
car_simulator:
module: JsCarSimulator
config_module:
auto_enable: true
connector_id: 1
auto_enable: true
connections:
simulation_control:
- module_id: yeti_driver
implementation_id: yeti_simulation_control
ev:
- module_id: iso15118_car
implementation_id: ev
slac:
- module_id: slac
implementation_id: ev
ocpp:
module: OCPP
config_module:
ChargePointConfigPath: ocpp16-config.json
UserConfigPath: user_config.json
EnableExternalWebsocketControl: true
connections:
evse_manager:
- module_id: connector_1
implementation_id: evse
reservation:
- module_id: auth
implementation_id: reservation
auth:
- module_id: auth
implementation_id: main
system:
- module_id: system
implementation_id: main
auth:
module: Auth
config_module:
connection_timeout: 20
connections:
token_provider:
- module_id: token_provider_manual
implementation_id: main
- module_id: ocpp
implementation_id: auth_provider
token_validator:
- module_id: ocpp
implementation_id: auth_validator
evse_manager:
- module_id: connector_1
implementation_id: evse
token_provider_manual:
module: JsDummyTokenProviderManual
connections: {}
config_implementation:
main:
token: '123'
type: dummy
energy_manager:
module: EnergyManager
connections:
energy_trunk:
- module_id: grid_connection_point
implementation_id: energy_grid
grid_connection_point:
module: EnergyNode
config_module:
fuse_limit_A: 63.0
phase_count: 3
connections:
price_information: []
energy_consumer:
- module_id: connector_1
implementation_id: energy_grid
powermeter:
- module_id: yeti_driver
implementation_id: powermeter
system:
module: System
x-module-layout: {}

View File

@@ -0,0 +1,171 @@
active_modules:
iso15118_charger:
module: PyJosev
config_module:
device: auto
supported_DIN70121: false
iso15118_car:
module: JsCarV2G
config_implementation:
main:
stack_implementation: RISE-V2G
mqtt_base_path: everest_external/iso15118/ev
device: auto
connector_1:
module: EvseManager
config_module:
connector_id: 1
three_phases: true
has_ventilation: true
country_code: DE
rcd_enabled: true
evse_id: "1"
session_logging: true
session_logging_xml: false
ac_hlc_enabled: false
ac_hlc_use_5percent: false
ac_enforce_hlc: false
connections:
bsp:
- module_id: yeti_driver_1
implementation_id: board_support
powermeter_grid_side:
- module_id: yeti_driver_1
implementation_id: powermeter
slac:
- module_id: slac
implementation_id: evse
hlc:
- module_id: iso15118_charger
implementation_id: charger
connector_2:
module: EvseManager
config_module:
connector_id: 2
three_phases: true
has_ventilation: true
country_code: DE
rcd_enabled: true
evse_id: "2"
session_logging: true
session_logging_xml: false
ac_hlc_enabled: false
ac_hlc_use_5percent: false
ac_enforce_hlc: false
connections:
bsp:
- module_id: yeti_driver_2
implementation_id: board_support
powermeter_grid_side:
- module_id: yeti_driver_2
implementation_id: powermeter
slac:
- module_id: slac
implementation_id: evse
hlc:
- module_id: iso15118_charger
implementation_id: charger
yeti_driver_1:
module: JsYetiSimulator
yeti_driver_2:
module: JsYetiSimulator
slac:
module: JsSlacSimulator
car_simulator_1:
module: JsCarSimulator
config_module:
connector_id: 1
auto_enable: true
auto_exec: false
auto_exec_commands: sleep 1;iec_wait_pwr_ready;sleep 1;draw_power_regulated 16,3;sleep 30;unplug
connections:
simulation_control:
- module_id: yeti_driver_1
implementation_id: yeti_simulation_control
ev:
- module_id: iso15118_car
implementation_id: ev
slac:
- module_id: slac
implementation_id: ev
car_simulator_2:
module: JsCarSimulator
config_module:
connector_id: 2
auto_enable: true
auto_exec: false
connections:
simulation_control:
- module_id: yeti_driver_2
implementation_id: yeti_simulation_control
ev:
- module_id: iso15118_car
implementation_id: ev
slac:
- module_id: slac
implementation_id: ev
ocpp:
module: OCPP201
config_module:
ChargePointConfigPath: config.json
connections:
evse_manager:
- module_id: connector_1
implementation_id: evse
- module_id: connector_2
implementation_id: evse
system:
- module_id: system
implementation_id: main
auth:
module: Auth
config_module:
connection_timeout: 30
selection_algorithm: PlugEvents
connections:
token_provider:
- module_id: ocpp
implementation_id: auth_provider
- module_id: token_provider_manual
implementation_id: main
token_validator:
- module_id: ocpp
implementation_id: auth_validator
evse_manager:
- module_id: connector_1
implementation_id: evse
- module_id: connector_2
implementation_id: evse
token_provider_manual:
module: JsDummyTokenProviderManual
energy_manager:
module: EnergyManager
connections:
energy_trunk:
- module_id: grid_connection_point
implementation_id: energy_grid
grid_connection_point:
module: EnergyNode
config_module:
fuse_limit_A: 40.0
phase_count: 3
connections:
price_information: []
energy_consumer:
- module_id: connector_1
implementation_id: energy_grid
- module_id: connector_2
implementation_id: energy_grid
powermeter:
- module_id: yeti_driver_1
implementation_id: powermeter
api:
module: API
connections:
evse_manager:
- module_id: connector_1
implementation_id: evse
system:
module: System
x-module-layout: {}

View File

@@ -0,0 +1,59 @@
{
"Internal": {
"ChargePointId": "cp001",
"CentralSystemURI": "127.0.0.1:9000/cp001",
"ChargeBoxSerialNumber": "cp001",
"ChargePointModel": "Yeti",
"ChargePointVendor": "Pionix",
"FirmwareVersion": "0.1",
"LogMessages": true
},
"Core": {
"AllowOfflineTxForUnknownId": true,
"AuthorizeRemoteTxRequests": true,
"AuthorizationCacheEnabled": true,
"ClockAlignedDataInterval": 900,
"ConnectionTimeOut": 10,
"ConnectorPhaseRotation": "0.RST,1.RST",
"GetConfigurationMaxKeys": 100,
"HeartbeatInterval": 86400,
"LocalAuthorizeOffline": false,
"LocalPreAuthorize": false,
"MeterValuesAlignedData": "Energy.Active.Import.Register",
"MeterValuesSampledData": "Energy.Active.Import.Register",
"MeterValueSampleInterval": 0,
"NumberOfConnectors": 1,
"ResetRetries": 1,
"StopTransactionOnEVSideDisconnect": true,
"StopTransactionOnInvalidId": true,
"StopTxnAlignedData": "Energy.Active.Import.Register",
"StopTxnSampledData": "Energy.Active.Import.Register",
"SupportedFeatureProfiles": "Core,FirmwareManagement,RemoteTrigger,Reservation,LocalAuthListManagement,SmartCharging",
"TransactionMessageAttempts": 3,
"TransactionMessageRetryInterval": 1,
"UnlockConnectorOnEVSideDisconnect": true
},
"FirmwareManagement": {
"SupportedFileTransferProtocols": "FTP"
},
"Security": {
"AuthorizationKey": "AABBCCDDEEFFGGHH",
"SecurityProfile": 0,
"CpoName": "Pionix",
"AdditionalRootCertificateCheck": false
},
"LocalAuthListManagement": {
"LocalAuthListEnabled": true,
"LocalAuthListMaxLength": 42,
"SendLocalListMaxLength": 42
},
"Reservation": {
"ReserveConnectorZeroSupported": true
},
"SmartCharging": {
"ChargeProfileMaxStackLevel": 42,
"ChargingScheduleAllowedChargingRateUnit": "Current,Power",
"ChargingScheduleMaxPeriods": 42,
"MaxChargingProfilesInstalled": 42
}
}

View File

@@ -0,0 +1,14 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
import shutil
def pytest_addoption(parser):
parser.addoption("--everest-prefix", action="store", default="~/checkout/everest-workspace/EVerest",
help="EVerest path; default = '~/checkout/everest-workspace/EVerest'")
parser.addoption("--libocpp", action="store", default="~/checkout/everest-workspace/libocpp",
help="libocpp path; default = '~/checkout/everest-workspace/libocpp'")
def pytest_configure(config):
everest_prefix = config.getoption("--everest-prefix")
shutil.copy("conf/ocpp16-config.json", f"{everest_prefix}/share/everest/modules/OCPP")

View File

@@ -0,0 +1,192 @@
import pytest
import asyncio
from datetime import datetime
from ocpp.v201 import call_result, call
from ocpp.v201.datatypes import SetVariableResultType, IdTokenType
from ocpp.v201.enums import SetVariableStatusType, IdTokenType as IdTokenTypeEnum, ClearCacheStatusType, ConnectorStatusType, RequestStartStopStatusType
from everest.testing.core_utils.controller.everest_test_controller import EverestTestController
from everest.testing.core_utils.controller.test_controller_interface import TestController
from everest.testing.core_utils.fixtures import *
# noinspection PyUnresolvedReferences
from everest.testing.ocpp_utils.fixtures import test_utility, charge_point_v16, central_system, test_config, ocpp_config, charge_point, ocpp_version
from everest.testing.ocpp_utils.charge_point_utils import wait_for_and_validate, OcppTestConfiguration, TestUtility
from everest.testing.ocpp_utils.charge_point_v201 import ChargePoint201
from everest.testing.ocpp_utils.charge_point_v16 import ChargePoint16
def validate_status_notification_201(meta_data, msg, exp_payload):
return msg.payload['connectorStatus'] == exp_payload.connector_status and \
msg.payload['evseId'] == exp_payload.evse_id and \
msg.payload['connectorId'] == exp_payload.connector_id
@ pytest.mark.asyncio
@pytest.mark.ocpp_version("ocpp1.6")
@pytest.mark.everest_core_config("conf/everest-config-ocpp16.yaml")
async def test_ocpp_16(test_config: OcppTestConfiguration, charge_point_v16: ChargePoint16, test_controller: TestController, test_utility: TestUtility):
await charge_point_v16.get_configuration_req(key=["AuthorizeRemoteTxRequests"])
await charge_point_v16.change_configuration_req(key="MeterValueSampleInterval", value="10")
# send RemoteStartTransaction.req
await charge_point_v16.remote_start_transaction_req(id_tag="DEADBEEF", connector_id=1)
assert await wait_for_and_validate(test_utility, charge_point_v16, "RequestStartTransactio", call_result.RequestStartTransactionPayload(status=RequestStartStopStatusType.accepted))
assert await wait_for_and_validate(test_utility, charge_point_v16, "StatusNotification", {"connectorId": 1, "status": "Preparing"})
test_controller.plug_in()
# expect StartTransaction.req
assert await wait_for_and_validate(test_utility, charge_point_v16, "StartTransaction", {
"connectorId": 1, "idTag": "DEADBEEF", "meterStart": 0})
assert await wait_for_and_validate(test_utility, charge_point_v16, "StatusNotification", {"connectorId": 1, "status": "Charging"})
assert await charge_point_v16.remote_stop_transaction_req(transaction_id=1)
assert await wait_for_and_validate(test_utility, charge_point_v16, "RemoteStopTransaction", {"status": "Accepted"})
assert await wait_for_and_validate(test_utility, charge_point_v16, "StopTransaction", {"reason": "Remote"})
assert await wait_for_and_validate(test_utility, charge_point_v16, "StatusNotification", {"connectorId": 1, "status": "Finishing"})
test_controller.plug_out()
assert await wait_for_and_validate(test_utility, charge_point_v16, "StatusNotification", {"connectorId": 1, "status": "Available"})
@ pytest.mark.asyncio
@pytest.mark.ocpp_version("ocpp2.0.1")
@pytest.mark.everest_core_config("conf/everest-config-ocpp201.yaml")
async def test_ocpp_201(charge_point_v201: ChargePoint201, test_controller: EverestTestController, test_utility: TestUtility):
"""This test case tests some requirements around AuthorizationCache of OCPP2.0.1
Args:
charge_point_v201 (ChargePoint201): this fixture starts up a OCPP2.0.1 CSMS and EVerest connection to this CSMS using OCPP. The reference can be used to send and receive messages over OCPP
test_controller (TestController): this fixture is used to control the simulation
test_utility (TestUtility): this fixture carries meta data of the test case that can be used for validations
"""
# prepare data for the test
evse_id = 1
connector_id = 1
# Enable AuthCacheCtrlr
r: call_result.SetVariablesPayload = await charge_point_v201.set_config_variables_req("AuthCacheCtrlr", "Enabled", "true")
set_variable_result: SetVariableResultType = SetVariableResultType(
**r.set_variable_result[0])
assert set_variable_result.attribute_status == SetVariableStatusType.accepted
# Enable LocalPreAuthorize
r: call_result.SetVariablesPayload = await charge_point_v201.set_config_variables_req("AuthCtrlr", "LocalPreAuthorize", "true")
set_variable_result: SetVariableResultType = SetVariableResultType(
**r.set_variable_result[0])
assert set_variable_result.attribute_status == SetVariableStatusType.accepted
# Set AuthCacheLifeTime
r: call_result.SetVariablesPayload = await charge_point_v201.set_config_variables_req("AuthCacheCtrlr", "LifeTime", "86400")
set_variable_result: SetVariableResultType = SetVariableResultType(
**r.set_variable_result[0])
assert set_variable_result.attribute_status == SetVariableStatusType.accepted
# Clear cache
r: call_result.ClearCachePayload = await charge_point_v201.clear_cache_req()
assert r.status == ClearCacheStatusType.accepted
test_controller.swipe("DEADBEEF")
assert await wait_for_and_validate(test_utility, charge_point_v201, "Authorize", call.AuthorizePayload(
id_token=IdTokenType(
id_token="DEADBEEF",
type=IdTokenTypeEnum.iso14443
)
))
test_controller.plug_in()
# eventType=Started
await wait_for_and_validate(test_utility, charge_point_v201, "TransactionEvent", {"eventType": "Started"})
test_utility.messages.clear()
test_controller.plug_out()
# eventType=Ended
await wait_for_and_validate(test_utility, charge_point_v201, "TransactionEvent", {"eventType": "Ended"})
test_utility.messages.clear()
await asyncio.sleep(2)
# because LocalPreAuthorize is true we dont expect an Authorize.req this time
test_utility.forbidden_actions.append("Authorize")
test_controller.swipe("DEADBEEF")
test_controller.plug_in()
assert await wait_for_and_validate(test_utility, charge_point_v201, "StatusNotification",
call.StatusNotificationPayload(datetime.now().isoformat(),
ConnectorStatusType.occupied, evse_id, connector_id),
validate_status_notification_201)
# because LocalPreAuthorize is true we dont expect an authorize here
r: call.TransactionEventPayload = call.TransactionEventPayload(**await wait_for_and_validate(test_utility, charge_point_v201,
"TransactionEvent", {"eventType": "Started"}))
# Disable LocalPreAuthorize
r: call_result.SetVariablesPayload = await charge_point_v201.set_config_variables_req("AuthCtrlr", "LocalPreAuthorize", "false")
set_variable_result: SetVariableResultType = SetVariableResultType(
**r.set_variable_result[0])
assert set_variable_result.attribute_status == SetVariableStatusType.accepted
# Set AuthCacheLifeTime to 1s
r: call_result.SetVariablesPayload = await charge_point_v201.set_config_variables_req("AuthCacheCtrlr", "LifeTime", "1")
set_variable_result: SetVariableResultType = SetVariableResultType(
**r.set_variable_result[0])
assert set_variable_result.attribute_status == SetVariableStatusType.accepted
test_utility.messages.clear()
test_controller.plug_out()
assert await wait_for_and_validate(test_utility, charge_point_v201, "StatusNotification",
call.StatusNotificationPayload(datetime.now().isoformat(),
ConnectorStatusType.available, evse_id, connector_id),
validate_status_notification_201)
# eventType=Ended
await wait_for_and_validate(test_utility, charge_point_v201,"TransactionEvent", {"eventType": "Ended"})
test_utility.forbidden_actions.clear()
test_controller.swipe("DEADBEEF")
assert await wait_for_and_validate(test_utility, charge_point_v201, "Authorize", call.AuthorizePayload(
id_token=IdTokenType(
id_token="DEADBEEF",
type=IdTokenTypeEnum.iso14443
)
))
# Enable LocalPreAuthorize
r: call_result.SetVariablesPayload = await charge_point_v201.set_config_variables_req("AuthCtrlr", "LocalPreAuthorize", "true")
set_variable_result: SetVariableResultType = SetVariableResultType(
**r.set_variable_result[0])
assert set_variable_result.attribute_status == SetVariableStatusType.accepted
test_controller.plug_in()
# eventType=Started
assert await wait_for_and_validate(test_utility, charge_point_v201, "TransactionEvent", {"eventType": "Started"})
test_utility.messages.clear()
test_controller.plug_out()
# eventType=Ended
assert await wait_for_and_validate(test_utility, charge_point_v201,"TransactionEvent", {"eventType": "Ended"})
assert await wait_for_and_validate(test_utility, charge_point_v201, "StatusNotification",
call.StatusNotificationPayload(datetime.now().isoformat(),
ConnectorStatusType.available, evse_id, connector_id),
validate_status_notification_201)
# AuthCacheLifeTime expired so sending Authorize.req
test_controller.swipe("DEADBEEF")
assert await wait_for_and_validate(test_utility, charge_point_v201, "Authorize", call.AuthorizePayload(
id_token=IdTokenType(
id_token="DEADBEEF",
type=IdTokenTypeEnum.iso14443
)
))