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

16
TODO.md
View File

@@ -36,13 +36,15 @@ FlexMeasures = élément central. Volet R&D LEM (Local Energy Market).
| cariflex-openocpp | Déployer OpenOCPP | Firmware OCPP 1.6J/2.0.1 pour bornes EV |
| cariflex-opendsm | Déployer OpenDSM | Calcul consommation normalisée |
| cariflex-openstef | Déployer OpenSTEF | Prévision énergétique open-source |
| cariflex-shapeshifter | Déployer ShapeShifter | Flexibility market platform |
| cariflex-everest | Déployer EVerest | EV charging middleware |
| cariflex-s2-cem | Déployer S2 CEM | flexmeasures-client[s2] |
| cariflex-openleadr | OpenLEADR | Passerelle OpenADR 2.0b pour DSO |
| cariflex-oplem | Installer OPLEM | `pip install oplem` |
| cariflex-dashboard | Dashboard Cariflex | Flexibilité + signaux DSO |
| cariflex-logo | Logo Cariflex | Remplacer logo FM |
| cariflex-citrineos | Déployer CitrineOS | CSMS OCPP 2.0.1 pour bornes VE | ✅ Extrait |
| cariflex-openocpp | Déployer OpenOCPP | Firmware OCPP 1.6J/2.0.1 pour bornes EV | ✅ Extrait |
| cariflex-shapeshifter | Déployer ShapeShifter | Flexibility market platform | ✅ Installé |
| cariflex-s2-cem | Déployer S2 CEM | flexmeasures-client[s2] | — |
| cariflex-flex-config | Config flex_context assets | flex_context + flex_model | — |
| cariflex-gireve | Intégrer GIREVE | Interopérabilité recharge EV | — |
| cariflex-omega-x | Intégrer OMEGA-X | Data space énergie EU | GitHub inaccessible |
| cariflex-dashboard | Dashboard Cariflex | Flexibilité + signaux DSO | — |
| cariflex-logo | Logo Cariflex | Remplacer logo FM | Téléchargement logo |
| cariflex-hal | Article HAL | Extraire texte PDF |
| cariflex-flex-config | Config flexibilité | flex_context + flex_model par type |
## Actifs FlexMeasures (40)

32
docs/github_repos.md Normal file
View File

@@ -0,0 +1,32 @@
# Cariflex - Repos GitHub à cloner (GitHub inaccessible actuellement)
## À cloner manuellement quand l'accès GitHub sera rétabli
| Repo | URL | Usage |
|------|-----|-------|
| CitrineOS | https://github.com/lfenergy/citrineos.git | CSMS OCPP 2.0.1 |
| OpenOCPP | https://github.com/ChargeLab/OpenOCPP.git | Firmware OCPP bornes |
| EVerest | https://github.com/EVerest/EVerest.git | Middleware EV charging |
| ShapeShifter | https://github.com/shapeshifter/shapeshifter.git | Flexibility market |
| OMEGA-X | https://github.com/OMEGA-X-EU/ | Data space énergie |
| EV4EU | https://github.com/EV4EU/ | Interopérabilité EV |
## Commandes de clonage (quand GitHub accessible)
```bash
cd /home/eric/cariflex/tools
# CitrineOS
git clone https://github.com/lfenergy/citrineos.git
cd citrineos && docker compose up -d
# OpenOCPP
git clone https://github.com/ChargeLab/OpenOCPP.git
# EVerest
git clone https://github.com/EVerest/EVerest.git
cd EVerest && docker compose up -d
# ShapeShifter
git clone https://github.com/shapeshifter/shapeshifter.git
```

106
scripts/configure_flex.py Normal file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""
Cariflex - Configure flex_context and flex_model for all 40 assets in FlexMeasures.
"""
import subprocess, json
def run_sql(query):
result = subprocess.run([
"docker", "exec", "flexmeasures-db", "psql", "-U", "flexmeasures", "-d", "flexmeasures",
"-t", "-c", query
], capture_output=True, text=True)
return result.stdout.strip()
# Flex context per asset type
FLEX_CONTEXTS = {
1: { # Panneau PV
"consumption-capacity": "0kW",
"production-capacity": "5kW",
"soc-min": "0kWh",
"soc-max": "0kWh"
},
2: { # Batterie
"consumption-capacity": "50kW",
"production-capacity": "50kW",
"soc-min": "10kWh",
"soc-max": "100kWh",
"charging-efficiency": "95%",
"discharging-efficiency": "95%"
},
3: { # Borne VE
"consumption-capacity": "22kW",
"production-capacity": "0kW",
"soc-min": "0kWh",
"soc-max": "0kWh"
},
4: { # Véhicule EV (V2G)
"consumption-capacity": "11kW",
"production-capacity": "11kW",
"soc-min": "15kWh",
"soc-max": "75kWh",
"charging-efficiency": "95%",
"discharging-efficiency": "95%"
}
}
# Flex model per asset type (storage-specific)
FLEX_MODELS = {
2: { # Batterie
"soc-at-start": "50kWh",
"soc-target": "80kWh",
"soc-min": "10kWh",
"soc-max": "100kWh",
"roundtrip-efficiency": "90%",
"storage-efficiency": "99%",
"power-capacity": "50kW"
},
4: { # EV V2G
"soc-at-start": "40kWh",
"soc-target": "60kWh",
"soc-min": "15kWh",
"soc-max": "75kWh",
"roundtrip-efficiency": "90%",
"storage-efficiency": "99%",
"power-capacity": "11kW"
}
}
print("=== Configuring flex_context and flex_model for Cariflex assets ===\n")
# Get assets by type
for type_id, type_name in [(1, "PV"), (2, "Battery"), (3, "EV Charger"), (4, "EV V2G")]:
assets = run_sql(f"SELECT id, name FROM generic_asset WHERE generic_asset_type_id = {type_id} ORDER BY id;")
flex_context = json.dumps(FLEX_CONTEXTS.get(type_id, {}))
flex_model = json.dumps(FLEX_MODELS.get(type_id, {}))
# Escape single quotes for SQL
flex_context_sql = flex_context.replace("'", "''")
flex_model_sql = flex_model.replace("'", "''")
# Update all assets of this type
result = run_sql(f"""
UPDATE generic_asset
SET flex_context = '{flex_context_sql}'::jsonb,
flex_model = '{flex_model_sql}'::jsonb
WHERE generic_asset_type_id = {type_id};
""")
count = len([l for l in assets.split('\n') if l.strip()])
print(f"{type_name}: {count} assets updated")
print(f" flex_context: {flex_context}")
if type_id in FLEX_MODELS:
print(f" flex_model: {flex_model}")
print()
# Verify
print("=== Verification ===")
for type_id, type_name in [(1, "PV"), (2, "Battery"), (3, "EV Charger"), (4, "EV V2G")]:
sample = run_sql(f"""
SELECT name, flex_context::text, flex_model::text
FROM generic_asset
WHERE generic_asset_type_id = {type_id}
LIMIT 1;
""")
print(f" {type_name}: {sample}")
print("\n✅ Flex context and model configured for all 40 assets!")

View File

@@ -0,0 +1,10 @@
build
modules/framework
lib/everest/framework/bazel-bin
lib/everest/framework/bazel-framework
lib/everest/framework/bazel-out
lib/everest/framework/bazel-testlogs
modules/framework/bazel-bin
modules/framework/bazel-framework
modules/framework/bazel-out
modules/framework/bazel-testlogs

View File

@@ -0,0 +1,33 @@
# Re-enable autoloading for language-specific rules that were removed from
# Bazel 9 core. This is needed because many BCR modules haven't yet added
# explicit load() statements for rules_cc, rules_python, and rules_shell.
# This flag can be removed once all dependencies are updated for Bazel 9.
build --incompatible_autoload_externally=+cc_library,+cc_binary,+cc_test,+cc_shared_library,+cc_import,+cc_toolchain,+py_binary,+py_library,+py_test,+sh_binary,+sh_library,+sh_test
# Force -fPIC on C/C++ compilation so static archives can be linked into PIE
# binaries. Required because modern rustc defaults to producing PIE executables
# on x86_64-unknown-linux-gnu, and any static C++ library mixed into a Rust
# binary therefore has to be position-independent.
build --copt=-fPIC
# CI config: smaller binaries, shared repo cache, disk cache for artifacts.
build:ci -c opt
build:ci --strip=always
build:ci --copt=-g0
build:ci --repository_cache=~/.cache/bazel-repo-cache
build:ci --disk_cache=~/.cache/bazel-disk-cache
# workaround for goption.c:169:14: error: two or more data types in declaration specifiers on armv7
build:armv7-linux-gnueabihf --repo_env=BAZEL_CONLYOPTS="-std=gnu17"
build:aarch64-linux-gnu --repo_env=BAZEL_CONLYOPTS=""
build:aarch64-linux-musl --repo_env=BAZEL_CONLYOPTS=""
build:armv7-linux-musleabihf --repo_env=BAZEL_CONLYOPTS=""
# With our setup Bazel links every binary statically to `libframework.a` If you
# want to link dynamically to `libframework.so` use `--dynamic_mode=fully`.
# See https://bazel.build/docs/user-manual#dynamic-mode for more details. If
# linking dynamically, ensure that `libframework.so` is in the
# `LD_LIBRARY_PATH`.
#
# Not supported on musl targets.
# --dynamic_mode=default

View File

@@ -0,0 +1 @@
9.0.0

View File

@@ -0,0 +1,39 @@
version: "3.6"
services:
mqtt-server:
image: eclipse-mosquitto:2.0.10
volumes:
- type: bind
source: ./mosquitto/mosquitto.conf
target: /mosquitto/config/mosquitto.conf
logging:
driver: none
networks:
- testnetwork
e2e-test-server:
image: integration-image
depends_on:
- mqtt-server
environment:
- MQTT_SERVER_ADDRESS=mqtt-server
volumes:
- type: bind
source: ${GITHUB_WORKSPACE}
target: /ext
cap_add:
- NET_ADMIN
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
networks:
- testnetwork
networks:
testnetwork:
enable_ipv6: true
driver: bridge
ipam:
driver: default
config:
- subnet: "fd00:dead:beef::/64"

View File

@@ -0,0 +1,8 @@
# listener port-number [ip address/host name/unix socket path]
listener 1883
listener 9001
protocol websockets
allow_anonymous true

View File

@@ -0,0 +1,138 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
...

View File

@@ -0,0 +1,11 @@
Checks: >
*,
-llvmlibc*,
-fuchsia-default-arguments-calls,
-fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects,
-readability-function-cognitive-complexity,
-modernize-use-trailing-return-type,
-abseil-string-find-startswith,
-abseil-string-find-str-contains
HeaderFilterRegex: ".*"

View File

@@ -0,0 +1 @@
.env

View File

@@ -0,0 +1,64 @@
volumes:
ocpp-db-data:
external: false
node-red-data:
external: false
services:
mqtt-server:
build: ../applications/containers/mosquitto
ports:
- 1883:1883
- 9001:9001
profiles:
- all
- mqtt
- ocpp
- sil
ocpp-db:
image: mariadb:10.4.30 # pinned to patch-version because https://github.com/steve-community/steve/pull/1213
volumes:
- ocpp-db-data:/var/lib/mysql
ports:
- 13306:3306
environment:
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
MYSQL_DATABASE: ocpp-db
MYSQL_USER: ocpp
MYSQL_PASSWORD: ocpp
profiles:
- all
- ocpp
steve:
build: ../applications/containers/steve
ports:
- 8180:8180
- 8443:8443
depends_on:
- ocpp-db
profiles:
- all
- ocpp
mqtt-explorer:
build: ../applications/containers/mqtt-explorer
depends_on:
- mqtt-server
ports:
- 4000:4000
profiles:
- all
- sil
nodered:
build: ../applications/containers/nodered
ports:
- 1880:1880
volumes:
- node-red-data:/data
environment:
- NODE_RED_ENABLE_PROJECTS=false
- MQTT_BROKER=mqtt-server
- MQTT_PORT=1883
profiles:
- all
- sil

View File

@@ -0,0 +1,86 @@
# syntax=docker/dockerfile:1
FROM ghcr.io/everest/everest-ci/dev-env-base:v1.5.4
# Build arguments for customization
# User and group configuration i.e. to match host user inside the container
ARG USERNAME=docker
ARG USER_UID=1000
ARG USER_GID=1000
# Configuration for everest-dev-tool's default git settings, can be overridden when working i.e. in a fork
ARG EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION_ARG=EVerest
ARG EVEREST_DEV_TOOL_DEFAULT_GIT_HOST_ARG=github.com
ARG EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER_ARG=git
##################################################################
# Should be moved to everest-dev-base at some point
# Update the package list and install dependencies (run as root)
USER root
RUN apt-get update && apt-get install -y \
protobuf-compiler \
libsystemd-dev \
libboost-log-dev \
tmux \
chrpath \
cpio \
diffstat \
gawk \
wget \
zstd \
liblz4-tool \
file \
iproute2 \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y locales \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
&& dpkg-reconfigure --frontend=noninteractive locales \
&& update-locale LANG=en_US.UTF-8
ENV LANG en_US.UTF-8
# EVerest Development Tool - Dependencies
RUN pip install --break-system-packages \
nanopb \
pytest
# EVerest Development Tool
COPY --from=everest_dev_tool_src . /tmp/everest_dev_tool
RUN ls -al /tmp/everest_dev_tool \
&& python3 -m pip install \
--break-system-packages \
/tmp/everest_dev_tool/ \
&& rm -rf /tmp/everest_dev_tool
##################################################################
# Modify the existing docker user and group to match the host's USER_UID and USER_GID
RUN groupmod --gid ${USER_GID} ${USERNAME} && \
usermod --uid ${USER_UID} --gid ${USER_GID} ${USERNAME} && \
usermod -aG dialout ${USERNAME}
# Switch to the docker user
USER ${USERNAME}
WORKDIR /workspace
# Add known hosts for git repositories
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan ${EVEREST_DEV_TOOL_DEFAULT_GIT_HOST_ARG} >> ~/.ssh/known_hosts
# Set environment variables
ENV EVEREST_DEV_TOOL_DEFAULT_GIT_METHOD=ssh
ENV EVEREST_DEV_TOOL_DEFAULT_GIT_HOST=${EVEREST_DEV_TOOL_DEFAULT_GIT_HOST_ARG}
ENV EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER=${EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER_ARG}
ENV EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION=${EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION_ARG}
# Enable command line completion for devrd script by default
RUN echo "" >> ${HOME}/.bashrc && \
echo "# Enable devrd command completion if available" >> ${HOME}/.bashrc && \
echo "if [ -f /workspace/applications/devrd/devrd-completion.bash ]; then" >> ${HOME}/.bashrc && \
echo " source /workspace/applications/devrd/devrd-completion.bash" >> ${HOME}/.bashrc && \
echo "fi" >> ${HOME}/.bashrc
# Set up welcome message
RUN echo "echo \"🏔️ 🚘 Welcome to the EVerest development environment!\"" >> ${HOME}/.bashrc && \
echo "echo \"Since you are working with the EVerest monorepo, you have all you need to get started here. 🎉🎉🎉\"" >> ${HOME}/.bashrc && \
echo "echo \"You can find the devrd tool in applications/devrd/devrd\"" >> ${HOME}/.bashrc

View File

@@ -0,0 +1,80 @@
{
"name": "EVerest - ${localWorkspaceFolderBasename}",
// Compose configuration
"dockerComposeFile": [
"../docker-compose.yml",
"./docker-compose.devcontainer.yml"
],
"service": "devcontainer",
"runServices": [ "mqtt-server", "ocpp-db", "steve", "mqtt-explorer" ],
// Workspace inside the container
"workspaceFolder": "/workspace",
"initializeCommand": "./applications/devrd/devrd env",
"updateRemoteUserUID": false,
// Networking / forwarded ports
"remoteUser": "docker",
"forwardPorts": [
"mqtt-explorer:4000",
"steve:8180"
],
"portsAttributes": {
"mqtt-explorer:4000": {
"label": "MQTT Explorer - WebView"
},
"steve:8180": {
"label": "Steve - WebTool"
}
},
"otherPortsAttributes": {
"onAutoForward": "notify",
"protocol": "http",
"requireLocalPort": false
},
// VS Code customizations
"customizations": {
// Be aware that anything set here will not apply to the devcontainer
// when used without VS Code (e.g., plain Docker Compose).
"vscode": {
"settings": {
"terminal.integrated.profiles.linux": {
"bash": {
"path": "/bin/bash",
"icon": "terminal-bash",
"args": ["-l"]
}
},
"terminal.integrated.defaultProfile.linux": "bash",
"python.pythonPath": "/usr/bin/python3",
"python.defaultInterpreterPath": "/usr/bin/python3",
"editor.rulers": [79, 120],
// RST
"restructuredtext.preview.scrollEditorWithPreview": false,
"restructuredtext.pythonRecommendation.disabled": true
},
"extensions": [
// language support CPP
"ms-vscode.cpptools",
// language support cmake
"twxs.cmake",
"ms-vscode.cmake-tools",
// language support python
"ms-python.python",
// language support reStructuredText
"lextudio.restructuredtext",
"trond-snekvik.simple-rst"
]
}
}
}

View File

@@ -0,0 +1,82 @@
networks:
docker-proxy-network:
internal: true
devcontainer-ipv6:
enable_ipv6: true
driver_opts:
com.docker.network.endpoint.sysctls: "net.ipv6.conf.eth0.disable_ipv6=0"
volumes:
cpm-source-cache:
name: everest-cpm-source-cache
services:
docker-proxy:
image: tecnativa/docker-socket-proxy:latest
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
environment:
- CONTAINERS=1
- IMAGES=1
- POST=1
- NETWORKS=1
- VOLUMES=1
networks:
- docker-proxy-network
profiles:
- all
- ocpp
- sil
devcontainer:
depends_on:
- docker-proxy
env_file:
- .env
build:
dockerfile: Dockerfile
context: ./general-devcontainer
additional_contexts:
everest_dev_tool_src: ../applications/everest_dev_tool
args:
USER_UID: ${UID:-1000}
USER_GID: ${GID:-1000}
EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION_ARG: ${EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION:-EVerest}
EVEREST_DEV_TOOL_DEFAULT_GIT_HOST_ARG: ${EVEREST_DEV_TOOL_DEFAULT_GIT_HOST:-github.com}
EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER_ARG: ${EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER:-git}
volumes:
- type: bind
source: ${HOST_WORKSPACE_FOLDER:-..}
target: /workspace
- type: volume
source: cpm-source-cache
target: /home/docker/.cache/cpm
# Mount the host's SSH agent socket into the container
- type: bind
source: ${SSH_AUTH_SOCK}
target: /ssh-agent
command: sleep infinity
cap_add:
- NET_ADMIN
environment:
MQTT_SERVER_ADDRESS: mqtt-server
MQTT_SERVER_PORT: 1883
DOCKER_HOST: tcp://docker-proxy:2375
CPM_SOURCE_CACHE: /home/docker/.cache/cpm
# Tell SSH to use the forwarded agent
SSH_AUTH_SOCK: /ssh-agent
networks:
- docker-proxy-network
- default
- devcontainer-ipv6
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
- net.ipv6.conf.default.disable_ipv6=0
- net.ipv6.conf.lo.disable_ipv6=0
profiles:
- all
- ocpp
- sil

View File

@@ -0,0 +1,43 @@
{
"env": {
"browser": true,
"commonjs": true,
"es2021": true
},
"extends": [
"airbnb-base"
],
"parserOptions": {
"ecmaVersion": 12
},
"rules": {
"camelcase": "off",
"eqeqeq": [
"error",
"smart"
],
"comma-dangle": [
"warn",
{
"objects": "always-multiline",
"arrays": "always-multiline",
"functions": "never"
}
],
"import/no-unresolved": [
2,
{
"ignore": [
"everestjs"
]
}
],
"max-len": [
"warn",
{
"code": 120,
"tabWidth": 2
}
]
}
}

74
tools/EVerest-main/.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,74 @@
# default option unless later match takes precedence
* @pietfried @hikinggrass @corneliusclaussen @SebaLukas @andistorm @mlitre @djchhp @james-ctc @cburandt @florinmihut
# .ci
/.ci/ @pietfried @hikinggrass @corneliusclaussen @andistorm
# .github
/.github/ @pietfried @corneliusclaussen
/.github/workflows/ @pietfried @corneliusclaussen @andistorm
/.github/workflows/on_label.yaml @andistorm @nzbr
# applications
/applications/pionix_chargebridge @djchhp @corneliusclaussen
# cmake
/cmake/ @hikinggrass @a-w50 @corneliusclaussen @andistorm
# config
/config/ @hikinggrass @pietfried @corneliusclaussen @SebaLukas @mlitre
# lib
/lib/3rd_party/nanopb/ @a-w50 @corneliusclaussen @hikinggrass
/lib/everest/can_dpm1000/ @a-w50 @corneliusclaussen @hikinggrass
/lib/everest/evse_security/ @pietfried @hikinggrass @james-ctc
/lib/everest/gpio/ @corneliusclaussen @hikinggrass @hikinggrass
/lib/everest/conversions/ @hikinggrass @pietfried @corneliusclaussen @mlitre
/lib/everest/slac/ @a-w50 @corneliusclaussen @SebaLukas
/lib/everest/tls/ @james-ctc @mlitre @corneliusclaussen @SebaLukas @pietfried
/lib/everest/everest_api_types/ @hikinggrass @pietfried @corneliusclaussen @djchhp @cburandt @florinmihut
/lib/everest/io/ @djchhp @corneliusclaussen @hikinggrass @james-ctc @pietfried
# modules
/modules/API/ @hikinggrass @pietfried @corneliusclaussen @djchhp @SebaLukas @james-ctc @cburandt @florinmihut
/modules/API/RpcApi/ @FaHaGit @barsnick
/modules/EnergyManagement/Energymanager/ @corneliusclaussen @hikinggrass @pietfried
/modules/EnergyManagement/EnergyNode/ @corneliusclaussen @hikinggrass @pietfried
/modules/EV/EvManager/ @SebaLukas @pietfried @james-ctc @mlitre
/modules/EV/PyEvJosev/ @SebaLukas @corneliusclaussen @pietfried @james-ctc @mlitre
/modules/EVSE/Auth/ @pietfried @corneliusclaussen @hikinggrass @mlitre
/modules/EVSE/Evse15118D20/ @SebaLukas @a-w50 @corneliusclaussen @pietfried @hikinggrass @james-ctc @mlitre
/modules/EVSE/EvseManager/ @corneliusclaussen @SebaLukas @hikinggrass @pietfried @mlitre
/modules/EVSE/EvseSecurity/ @pietfried @hikinggrass @james-ctc @mlitre @SebaLukas
/modules/EVSE/EvseSlac/ @a-w50 @corneliusclaussen @SebaLukas @pietfried @hikinggrass
/modules/EVSE/EvseV2G/ @corneliusclaussen @SebaLukas @james-ctc @pietfried @hikinggrass @james-ctc @mlitre @barsnick @FaHaGit
/modules/EVSE/OCPP/ @hikinggrass @pietfried @mlitre
/modules/HardwareDrivers/ @pietfried @hikinggrass @corneliusclaussen @florinmihut @cburandt @djchhp
/modules/EVSE/OCPP201/ @hikinggrass @pietfried @mlitre
/modules/Examples/CppExamples/error-framework/ @corneliusclaussen @hikinggrass @pietfried @andistorm
/modules/Examples/RustExamples/ @SirVer @dorezyuk
/modules/HardwareDrivers/EVSE/PhyVersoBSP/ @pietfried @hikinggrass @corneliusclaussen @dorezyuk @rckstrh
/modules/HardwareDrivers/EVSE/YetiDriver/ @corneliusclaussen @hikinggrass
/modules/Misc/ErrorHistory/ @corneliusclaussen @hikinggrass @pietfried @andistorm
/modules/Misc/PacketSniffer/ @corneliusclaussen @SebaLukas @hikinggrass
/modules/Misc/Setup/ @hikinggrass @corneliusclaussen @pietfried
/modules/Simulation/ @SebaLukas @pietfried @hikinggrass
/modules/Simulation/SlacSimulator/ @SebaLukas @pietfried @corneliusclaussen @james-ctc @mlitre
/modules/Simulation/YetiSimulator/ @SebaLukas @pietfried @corneliusclaussen @james-ctc @mlitre
**/Cargo.toml @SirVer @dorezyuk @pietfried @hikinggrass
**/Cargo.lock @SirVer @dorezyuk @pietfried @hikinggrass
# Rust & Bazel
*.rs @SirVer @dorezyuk @pietfried @hikinggrass
*.bazel @SirVer @dorezyuk @pietfried @hikinggrass
*.bzl @SirVer @dorezyuk @pietfried @hikinggrass
# third-party/bazel
/third-party/bazel/deps_versions.bzl @pietfried @hikinggrass @corneliusclaussen @SebaLukas @a-w50 @SirVer @dorezyuk
/dependencies.yaml @hikinggrass @pietfried @corneliusclaussen @SebaLukas @james-ctc @mlitre
/module-dependencies.cmake @hikinggrass @pietfried @corneliusclaussen @SebaLukas @james-ctc @mlitre
# yocto
/yocto/ @hikinggrass @corneliusclaussen @james-ctc @andistorm @barsnick

View File

@@ -0,0 +1,77 @@
name: Bug Report
description: Thanks for taking the time to fill out this bug report!
body:
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If applicable, add screenshots or log files to help explain your problem.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: What did you expect to happen?
- type: dropdown
id: affected-domain
attributes:
label: EVerest Domain
description: Which domain is affected by the issue? Mark multiple if applicable.
options:
- Authorization
- Build System
- Charge Control
- CHAdeMO
- DIN70121
- Documentation
- Energy Management
- Framework
- Hardware Drivers
- ISO 15118-2
- ISO 15118-20
- OCPP 1.6
- OCPP 2.0.1
- OCPP 2.1
- Security
- Simulation
- SLAC
- Testing
- Utilities
- Other
multiple: true
validations:
required: true
- type: textarea
id: affected-component
attributes:
label: Affected Component
description: |
Please specify the component where the bug is located, if known.
Examples: a module (e.g. `modules/EnergyManagement`), a library (e.g. `lib/ocpp`, `lib/iso15118`),
or an application (e.g. `applications/everest_dev_tool`). Leave empty if unsure.
- type: input
id: version
attributes:
label: EVerest Version
description: Git tag, release version, or commit hash (e.g. `2026.02.0`, `abc1234`).
placeholder: e.g. 2026.02.0
- type: textarea
id: reproduction
attributes:
label: To Reproduce
description: |
Describe the steps and any additional information needed to reproduce the behavior:
EVerest configuration files, compile options, and system information.
- type: textarea
id: other
attributes:
label: Anything else?
placeholder: Add any other context about the bug report here.

View File

@@ -0,0 +1,70 @@
name: Feature Request
description: Thanks for taking the time to fill out this feature request!
body:
- type: textarea
id: description
attributes:
label: Describe the problem
description: What problem is your feature request targeting and why is it a problem? Please describe.
placeholder: A clear and concise description of what the problem is.
validations:
required: true
- type: dropdown
id: affected-domain
attributes:
label: EVerest Domain
description: Which domain is this feature request for? Mark multiple if applicable.
options:
- Authorization
- Build System
- Charge Control
- CHAdeMO
- DIN70121
- Documentation
- Energy Management
- Framework
- Hardware Drivers
- ISO 15118-2
- ISO 15118-20
- OCPP 1.6
- OCPP 2.0.1
- OCPP 2.1
- Security
- Simulation
- SLAC
- Testing
- Utilities
- Other
multiple: true
validations:
required: true
- type: textarea
id: affected-component
attributes:
label: Affected Component
description: |
Can you specify where the feature should be implemented, if known?
Examples: a module (e.g. `modules/EnergyManagement`), a library (e.g. `lib/ocpp`, `lib/iso15118`),
or an application (e.g. `applications/everest_dev_tool`). Leave empty if unsure.
- type: textarea
id: solution
attributes:
label: Describe your solution
description: Describe the solution you'd like.
placeholder: A clear and concise description of what you want to happen.
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Have you considered alternative solutions or workarounds? If so, describe them here.
- type: textarea
id: other
attributes:
label: Additional context
placeholder: Add any other context about the feature request here.

View File

@@ -0,0 +1,9 @@
## Describe your changes
## Issue ticket number and link
## Checklist before requesting a review
- [ ] I have performed a self-review of my code
- [ ] I have made corresponding changes to the documentation
- [ ] I read the [contribution documentation](https://everest.github.io/nightly/project/contributing.html) and made sure that my changes meet its requirements

View File

@@ -0,0 +1,92 @@
name: Create Coverage Badge
on:
workflow_call:
inputs:
runner:
description: 'Which runner to use'
required: false
default: 'ubuntu-24.04'
type: string
ref_everest_ci:
description: 'The ref of the everest-ci repository to checkout'
required: true
type: string
is_fork:
description: 'Whether the current repository is a fork'
required: true
type: string
artifact_deploy_target_repo:
description: 'Repository to deploy artifacts to'
required: true
type: string
coverage_report_artifact_name:
description: 'The name of the coverage report artifact to download'
required: true
type: string
coverage_xml_artifact_name:
description: 'The name of the coverage xml artifact to download'
required: true
type: string
secrets:
coverage_deploy_token:
description: 'The token to use to deploy the coverage report'
required: true
jobs:
create-coverage-badge:
name: Create Coverage Badge
runs-on: ${{ inputs.runner }}
steps:
- name: Checkout local github actions
uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/everest-ci
ref: ${{ inputs.ref_everest_ci }}
path: everest-ci
- name: Download xml coverage report
uses: actions/download-artifact@v5.0.0
with:
if-no-files-found: error
name: ${{ inputs.coverage_xml_artifact_name }}
path: coverage-xml
- name: Parse coverage report
id: parse_coverage_report
shell: python3 {0}
run: |
import xml.etree.ElementTree
import os
tree = xml.etree.ElementTree.parse("${{ github.workspace }}/coverage-xml/gcovr-coverage-xml.xml")
line_coverage = tree.getroot().get("line-rate")
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
f.write(f"line_coverage={line_coverage}\n")
f.write(f"line_coverage_percentage={float(line_coverage) * 100}\n")
- name: Generate coverage badge
run: |
pip install anybadge
mkdir -p ${{ github.workspace }}/coverage-badge/
anybadge -o --label Coverage --value ${{ steps.parse_coverage_report.outputs.line_coverage_percentage }} -s "%" --file ${{ github.workspace }}/coverage-badge/coverage-badge.svg 20=red 40=orange 60=yellow 80=yellowgreen 100=green
- name: Deploy coverage badge
uses: ./everest-ci/github-actions/deploy-ci-artifact
if: ${{ inputs.is_fork == 'false' }}
with:
target_repo: ${{ inputs.artifact_deploy_target_repo }}
github_token: ${{ secrets.coverage_deploy_token }}
artifact_name: coverage-badge
artifact_directory: ${{ github.workspace }}/coverage-badge/
deploy_global_artifact: true
- name: Download html coverage report
uses: actions/download-artifact@v5.0.0
with:
if-no-files-found: error
name: ${{ inputs.coverage_report_artifact_name }}
path: coverage-report
- name: Deploy html coverage report
uses: ./everest-ci/github-actions/deploy-ci-artifact
if: ${{ inputs.is_fork == 'false' }}
with:
target_repo: ${{ inputs.artifact_deploy_target_repo }}
github_token: ${{ secrets.coverage_deploy_token }}
artifact_name: ${{ inputs.coverage_report_artifact_name }}
artifact_directory: ${{ github.workspace }}/coverage-report/
deploy_global_artifact: true

View File

@@ -0,0 +1,35 @@
name: DCO Check
on:
workflow_call:
inputs:
runner:
description: 'Which runner to use'
required: false
default: 'ubuntu-24.04'
type: string
jobs:
dco_check:
name: DCO Check
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@v3
if: github.event_name == 'pull_request'
- name: Set up Python 3.x
if: github.event_name == 'pull_request'
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dco-check
if: github.event_name == 'pull_request'
run: pip3 install -U dco-check==0.5.0
- name: Check DCO pull_request
if: github.event_name == 'pull_request'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
dco-check --default-branch main -v --exclude-pattern ".*@users\.noreply\.github\.com"
- name: Check DCO merge_group
if: github.event_name == 'merge_group'
run: |
echo "DCO check is skipped for merge_group events."

View File

@@ -0,0 +1,131 @@
name: Integration Tests
on:
workflow_call:
inputs:
runner:
description: 'Which runner to use'
required: false
default: 'ubuntu-24.04'
type: string
build_kit_artifact_name:
description: 'The name of the build-kit artifact to download'
required: true
type: string
build_kit_image_tag:
description: 'The tag of the build-kit image to use for building the project'
required: true
type: string
build_kit_scripts_directory:
description: 'Directory in the repository where the build kit scripts are located'
required: false
default: '.ci/build-kit/scripts'
type: string
docker_compose_file_path:
description: 'The path to the docker-compose file, relative to the repository root'
required: false
default: '.ci/e2e/docker-compose.yaml'
type: string
test_service_name:
description: 'The name of the service to run integration tests on'
required: false
default: 'e2e-test-server'
type: string
result_xml_path:
description: 'The path to the result xml file, relative to the github workspace'
required: false
default: 'result.xml'
type: string
report_html_path:
description: 'The path to the report html file, relative to the github workspace'
required: false
default: 'report.html'
type: string
dist_artifact_name:
description: 'The name of the dist artifact to download'
required: true
type: string
wheels_artifact_name:
description: 'The name of the wheels artifact to download'
required: true
type: string
outputs:
integration_tests_artifact_name:
description: 'The name of the integration tests artifact'
value: ${{ jobs.integration-tests.outputs.integration_tests_artifact_name }}
jobs:
integration-tests:
name: Run Integration Tests
runs-on: ${{ inputs.runner }}
env:
INTEGRATION_IMAGE_NAME: integration-image
BUILD_KIT_IMAGE: ${{ inputs.build_kit_image_tag }}
INTEGRATION_TESTS_ARTIFACT_NAME: integration-tests-artifacts
outputs:
integration_tests_artifact_name: ${{ env.INTEGRATION_TESTS_ARTIFACT_NAME }}
steps:
- name: Download dist dir
uses: actions/download-artifact@v5.0.0
with:
name: ${{ inputs.dist_artifact_name }}
- name: Extract dist.tar.gz
run: |
tar -xzf ${{ github.workspace }}/dist.tar.gz -C ${{ github.workspace }}
- name: Download wheels
uses: actions/download-artifact@v5.0.0
with:
name: ${{ inputs.wheels_artifact_name }}
path: wheels
- name: Checkout repository
uses: actions/checkout@v4.2.2
with:
path: source
- name: Setup run scripts
run: |
mkdir scripts
rsync -a source/${{ inputs.build_kit_scripts_directory }}/ scripts
- name: Download build-kit image
uses: actions/download-artifact@v5
with:
name: ${{ inputs.build_kit_artifact_name }}
- name: Load build-kit image
run: |
docker load -i build-kit.tar
docker image tag ${{ env.BUILD_KIT_IMAGE }} build-kit
- name: Create integration-image
run: |
docker run \
--volume "${{ github.workspace }}:/ext" \
--name integration-container \
build-kit run-script create_integration_image
docker commit integration-container ${{ env.INTEGRATION_IMAGE_NAME }}
- name: Run integration tests
id: run_integration_tests
run: |
docker compose \
-f source/${{ inputs.docker_compose_file_path }} \
run \
${{ inputs.test_service_name }} \
tests/run-tests.sh integration \
--everest-prefix /ext/dist \
--junitxml /ext/${{ inputs.result_xml_path }} \
--html /ext/${{ inputs.report_html_path }}
- name: Upload result and report as artifact
if: ${{ always() && (steps.run_integration_tests.outcome == 'success' || steps.run_integration_tests.outcome == 'failure') }}
uses: actions/upload-artifact@v4.6.2
with:
if-no-files-found: error
name: ${{ env.INTEGRATION_TESTS_ARTIFACT_NAME }}
path: |
${{ inputs.result_xml_path }}
${{ inputs.report_html_path }}
- name: Render result
if: ${{ always() && (steps.run_integration_tests.outcome == 'success' || steps.run_integration_tests.outcome == 'failure') }}
uses: pmeier/pytest-results-action@v0.7.2
with:
path: ${{ inputs.result_xml_path }}
summary: True
display-options: fEX
fail-on-empty: True
title: Test results

View File

@@ -0,0 +1,36 @@
name: Lint Repository
on:
workflow_call:
inputs:
runner:
description: 'Which runner to use'
required: false
default: 'ubuntu-24.04'
type: string
ref_everest_ci:
description: 'The ref of the everest-ci repository to checkout'
required: true
type: string
jobs:
lint:
name: Lint Repository
runs-on: ${{ inputs.runner }}
steps:
- name: Checkout local github actions
uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/everest-ci
ref: ${{ inputs.ref_everest_ci }}
path: everest-ci
- name: Checkout repository
uses: actions/checkout@v4
with:
path: source
- name: Run clang-format
uses: ./everest-ci/github-actions/run-clang-format
with:
source-dir: source/
extensions: hpp,cpp
exclude: cache

View File

@@ -0,0 +1,115 @@
name: OCPP Tests
on:
workflow_call:
inputs:
runner:
description: 'Which runner to use'
required: false
default: 'ubuntu-24.04'
type: string
dist_artifact_name:
description: 'The name of the dist artifact to download'
required: true
type: string
wheels_artifact_name:
description: 'The name of the wheels artifact to download'
required: true
type: string
build_kit_artifact_name:
description: 'The name of the build-kit artifact to download'
required: true
type: string
build_kit_image_tag:
description: 'The tag of the build-kit image to use for building the project'
required: true
type: string
build_kit_scripts_directory:
description: 'Directory in the repository where the build kit scripts are located'
required: false
default: '.ci/build-kit/scripts'
type: string
outputs:
ocpp_tests_artifact_name:
description: 'The name of the OCPP tests artifact'
value: ${{ jobs.ocpp-tests.outputs.ocpp_tests_artifact_name }}
jobs:
ocpp-tests:
name: Run OCPP Tests
runs-on: ${{ inputs.runner }}
env:
OCPP_TESTS_ARTIFACT_NAME: ocpp-tests-artifacts
outputs:
ocpp_tests_artifact_name: ${{ env.OCPP_TESTS_ARTIFACT_NAME }}
steps:
- name: Download dist dir
uses: actions/download-artifact@v4.1.8
with:
name: ${{ inputs.dist_artifact_name }}
- name: Extract dist.tar.gz
run: |
tar -xzf ${{ github.workspace }}/dist.tar.gz -C ${{ github.workspace }}
- name: Download wheels
uses: actions/download-artifact@v4.1.8
with:
name: ${{ inputs.wheels_artifact_name }}
path: wheels
- name: Checkout repository
uses: actions/checkout@v4.2.2
with:
path: source
- name: Setup run scripts
run: |
mkdir scripts
rsync -a source/${{ inputs.build_kit_scripts_directory }}/ scripts
- name: Download build-kit image
uses: actions/download-artifact@v4
with:
name: ${{ inputs.build_kit_artifact_name }}
- name: Load build-kit image
run: |
docker load -i build-kit.tar
docker image tag ${{ inputs.build_kit_image_tag }} build-kit
- name: Create integration-image
run: |
docker run \
--volume "${{ github.workspace }}:/ext" \
--name integration-container \
build-kit run-script create_ocpp_tests_image
docker commit integration-container integration-image
- name: Run OCPP tests
id: run_ocpp_tests
continue-on-error: true
run: |
docker compose \
-f source/.ci/e2e/docker-compose.yaml \
run \
e2e-test-server \
tests/run-tests.sh ocpp \
--everest-prefix /ext/dist \
--junitxml /ext/ocpp-tests-result.xml \
--html /ext/ocpp-tests-report.html
- name: Upload result and report as artifact
continue-on-error: true
if: ${{ steps.run_ocpp_tests.outcome == 'success' || steps.run_ocpp_tests.outcome == 'failure' }}
uses: actions/upload-artifact@v4.4.3
with:
if-no-files-found: error
name: ${{ env.OCPP_TESTS_ARTIFACT_NAME }}
path: |
ocpp-tests-result.xml
ocpp-tests-report.html
- name: Render OCPP tests result
if: ${{ steps.run_ocpp_tests.outcome == 'success' || steps.run_ocpp_tests.outcome == 'failure' }}
uses: pmeier/pytest-results-action@v0.7.1
with:
path: ocpp-tests-result.xml
summary: True
display-options: fEX
fail-on-empty: True
title: Test results
- name: Check if OCPP tests failed
if: ${{ steps.run_ocpp_tests.outcome == 'failure' }}
run: exit 1

View File

@@ -0,0 +1,81 @@
name: Setup Environment
on:
workflow_call:
inputs:
runner:
description: 'Which runner to use'
required: false
default: 'ubuntu-24.04'
type: string
ref_everest_ci:
description: 'The reference of the everest-ci repository to checkout'
required: true
type: string
outputs:
ref_everest_ci:
description: 'The reference of the everest-ci repository to checkout'
value: ${{ inputs.ref_everest_ci }}
closest_tag_everest_ci:
description: 'The closest tag of the everest-ci repository to use'
value: ${{ jobs.setup-env.outputs.closest_tag_everest_ci }}
is_fork:
description: 'Whether the current repository is a fork'
value: ${{ jobs.setup-env.outputs.is_fork }}
jobs:
setup-env:
name: Setup Environment
runs-on: ${{ inputs.runner }}
outputs:
is_fork: ${{ steps.is_fork.outputs.is_fork }}
closest_tag_everest_ci: ${{ steps.set_tag_everest_ci.outputs.closest_tag }}
steps:
- name: Determine closest tag of everest-ci
id: set_tag_everest_ci
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TARGET_SHA="${{ steps.set_sha_everest_ci.outputs.sha }}"
OWNER="everest"
REPO="everest-ci"
COMMITS=$(gh api repos/$OWNER/$REPO/commits?sha=$TARGET_SHA\&per_page=100 --jq '.[].sha')
for COMMIT in $COMMITS; do
TAG=$(gh api repos/$OWNER/$REPO/tags --jq '.[] | select(.commit.sha == "'$COMMIT'") | .name')
if [ -n "$TAG" ]; then
break
fi
done
if [ -z "$TAG" ]; then
echo "No tag found for commit $TARGET_SHA (only last 100 commits were checked)"
exit 1
fi
# if inputs.build_kit_base_image_tag is != "", use it as the tag
if [ -n "${{ inputs.build_kit_base_image_tag }}" ]; then
echo "Using inputs.build_kit_base_image_tag as tag"
TAG="${{ inputs.build_kit_base_image_tag }}"
fi
echo "closest_tag=$TAG" >> $GITHUB_OUTPUT
- name: Determine whether the PR comes from fork
id: is_fork
run: |
if [ "${{ github.event_name }}" == "pull_request" ]; then
if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
is_fork=true
else
is_fork=false
fi
else
is_fork=false
fi
echo "is_fork=${is_fork}" >> $GITHUB_OUTPUT
if [ "${is_fork}" == "true" ]; then
echo "This is a forked PR"
else
echo "This is not a forked PR"
fi

View File

@@ -0,0 +1,51 @@
# Inspired by:
# Nixpkgs: https://github.com/NixOS/nixpkgs/blob/a698ac1214cd924d4394ca9cd2691618765aa03c/.github/workflows/backport.yml
# NixOS-WSL: https://github.com/nix-community/NixOS-WSL/blob/be894604b2aa2184c0b3d3b44995acd0da14dc0c/.github/workflows/on_label.yml
name: "Backport"
on:
pull_request_target:
types:
- closed
- labeled
jobs:
backport:
if: github.event.pull_request.labels && contains(join(github.event.pull_request.labels.*.name, ', '), 'backport ')
name: Backport PR 🔙
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
outputs:
created_pull_numbers: ${{ steps.backport.outputs.created_pull_numbers }}
steps:
- name: Check actor permissions
id: check-permissions
uses: prince-chrismc/check-actor-permissions-action@v3.0.2
with:
permission: write
- name: Generate App Token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.BACKPORT_APP_ID }}
private-key: ${{ secrets.BACKPORT_APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v6
- name: Create backport PR
id: backport
uses: korthout/backport-action@v4.5.1
with:
github_token: ${{ steps.app-token.outputs.token }}
merge_commits: "skip"
add_author_as_assignee: true
copy_requested_reviewers: true
experimental: >-
{
"conflict_resolution": "draft_commit_conflicts"
}

View File

@@ -0,0 +1,74 @@
name: Build, Lint and Test
on:
push:
branches:
- main
jobs:
setup-env:
name: Setup Environment
uses: ./.github/workflows/job_setup-env.yml
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
ref_everest_ci: v1.5.5
build-build-kit:
name: Build the build-kit
uses: ./.github/workflows/job_build-build-kit.yml
needs:
- setup-env
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
base_image_tag_everest_ci: ${{ needs.setup-env.outputs.closest_tag_everest_ci }}
build-cmake-gcc:
name: Build with CMake and GCC
uses: ./.github/workflows/job_build-cmake-gcc.yml
needs:
- setup-env
- build-build-kit
secrets:
coverage_deploy_token: "abcd"
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
ref_everest_ci: ${{ needs.setup-env.outputs.ref_everest_ci }}
is_fork: ${{ needs.setup-env.outputs.is_fork }}
build-kit-artifact-name: ${{ needs.build-build-kit.outputs.build_kit_artifact_name }}
build_kit_image_tag: ${{ needs.build-build-kit.outputs.build_kit_image_tag }}
artifact_deploy_target_repo: ${{ github.repository_owner }}/everest.github.io
# create-coverage-badge:
# uses: ./.github/workflows/job_create-coverage-badge.yml
# needs:
# - setup-env
# - build-cmake-gcc
# secrets:
# coverage_deploy_token: ${{ secrets.coverage_deploy_token }}
# with:
# runner: ${{ inputs.runner || 'ubuntu-24.04' }}
# ref_everest_ci: ${{ needs.setup-env.outputs.ref_everest_ci }}
# is_fork: ${{ needs.setup-env.outputs.is_fork }}
# artifact_deploy_target_repo: ${{ github.repository_owner }}/everest.github.io
# coverage_report_artifact_name: ${{ needs.build-cmake-gcc.outputs.coverage_report_artifact_name }}
# coverage_xml_artifact_name: ${{ needs.build-cmake-gcc.outputs.coverage_xml_artifact_name }}
build-docs:
name: Build and Deploy Documentation
needs:
- setup-env
- build-build-kit
uses: ./.github/workflows/job_build-docs.yaml
secrets:
SA_GITHUB_SSH_KEY: ${{ secrets.SA_GITHUB_SSH_KEY }}
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
is_fork: ${{ needs.setup-env.outputs.is_fork == 'true' }}
build_kit_artifact_name: ${{ needs.build-build-kit.outputs.build_kit_artifact_name }}
build_kit_image_tag: ${{ needs.build-build-kit.outputs.build_kit_image_tag }}
deploy_docs: true

View File

@@ -0,0 +1,139 @@
name: Build, Lint and Test
on:
pull_request: {}
merge_group: {}
workflow_dispatch:
inputs:
runner:
description: Which runner to use
type: choice
default: 'ubuntu-24.04'
required: true
options:
- 'ubuntu-24.04'
- 'large-ubuntu-24.04-xxl'
schedule:
- cron: '37 13,1 * * *'
jobs:
setup-env:
name: Setup Environment
uses: ./.github/workflows/job_setup-env.yml
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
ref_everest_ci: v1.5.5
build-build-kit:
name: Build the build-kit
uses: ./.github/workflows/job_build-build-kit.yml
needs:
- setup-env
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
base_image_tag_everest_ci: ${{ needs.setup-env.outputs.closest_tag_everest_ci }}
build-cmake-gcc:
name: Build with CMake and GCC
uses: ./.github/workflows/job_build-cmake-gcc.yml
needs:
- setup-env
- build-build-kit
secrets:
coverage_deploy_token: ""
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
ref_everest_ci: ${{ needs.setup-env.outputs.ref_everest_ci }}
is_fork: ${{ needs.setup-env.outputs.is_fork }}
build-kit-artifact-name: ${{ needs.build-build-kit.outputs.build_kit_artifact_name }}
build_kit_image_tag: ${{ needs.build-build-kit.outputs.build_kit_image_tag }}
artifact_deploy_target_repo: ${{ github.repository_owner }}/everest.github.io
integration-tests:
name: Run Integration Tests
uses: ./.github/workflows/job_integrations-tests.yml
needs:
- setup-env
- build-cmake-gcc
- build-build-kit
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
build_kit_artifact_name: ${{ needs.build-build-kit.outputs.build_kit_artifact_name }}
build_kit_image_tag: ${{ needs.build-build-kit.outputs.build_kit_image_tag }}
dist_artifact_name: ${{ needs.build-cmake-gcc.outputs.dist_artifact_name }}
wheels_artifact_name: ${{ needs.build-cmake-gcc.outputs.wheels_artifact_name }}
ocpp-tests:
name: Run OCPP Tests
uses: ./.github/workflows/job_ocpp-tests.yml
needs:
- setup-env
- build-cmake-gcc
- build-build-kit
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
build_kit_artifact_name: ${{ needs.build-build-kit.outputs.build_kit_artifact_name }}
build_kit_image_tag: ${{ needs.build-build-kit.outputs.build_kit_image_tag }}
dist_artifact_name: ${{ needs.build-cmake-gcc.outputs.dist_artifact_name }}
wheels_artifact_name: ${{ needs.build-cmake-gcc.outputs.wheels_artifact_name }}
lint:
name: Lint Repository
uses: ./.github/workflows/job_lint.yml
needs:
- setup-env
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
ref_everest_ci: ${{ needs.setup-env.outputs.ref_everest_ci }}
build-docs:
name: Call Build Documentation
needs:
- setup-env
- build-build-kit
uses: ./.github/workflows/job_build-docs.yaml
secrets:
SA_GITHUB_SSH_KEY: ${{ secrets.SA_GITHUB_SSH_KEY }}
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
is_fork: ${{ needs.setup-env.outputs.is_fork == 'true' }}
build_kit_artifact_name: ${{ needs.build-build-kit.outputs.build_kit_artifact_name }}
build_kit_image_tag: ${{ needs.build-build-kit.outputs.build_kit_image_tag }}
deploy_docs: false
dco-check:
name: DCO Check
uses: ./.github/workflows/job_dco-check.yaml
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
bazel-build-and-test:
name: Bazel Build And Test
uses: ./.github/workflows/job_bazel-build-test.yaml
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
bazel-cross-build:
name: Bazel Cross-Build (${{ matrix.platform }})
strategy:
fail-fast: false
matrix:
platform:
- aarch64-linux-gnu
- aarch64-linux-musl
- armv7-linux-gnueabihf
- armv7-linux-musleabihf
uses: ./.github/workflows/job_bazel-cross-build.yaml
with:
runner: ${{ inputs.runner || 'ubuntu-24.04' }}
platform: ${{ matrix.platform }}

17
tools/EVerest-main/.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
*build*
!lib/everest/*/cmake/*-build.cmake
*dist
.cache/
workspace.yaml
.vscode/
/bazel-*
/modules/target
CMakeLists.txt.user
.idea/
MODULE.bazel.lock
__pycache__/
*.patch
*.diff
*.orig
*.rej
docs/source/conf.py

View File

@@ -0,0 +1,3 @@
singleQuote: true
tabWidth: 2
quoteProps: consistent

View File

@@ -0,0 +1,22 @@
pep257:
disable:
- D203
- D212
- D213
- D214
- D215
- D404
- D405
- D406
- D407
- D408
- D409
- D410
- D411
- D413
- D415
- D416
- D417
pylint:
disable:
- logging-fstring-interpolation

View File

@@ -0,0 +1,8 @@
load("@bazel_skylib//rules:write_file.bzl", "write_file")
package(default_visibility = ["//visibility:public"])
exports_files([
"dependencies.yaml",
"MODULE.bazel",
])

View File

@@ -0,0 +1,255 @@
cmake_minimum_required(VERSION 3.16)
project(everest-core
VERSION 2026.02.0
DESCRIPTION "The open operating system for e-mobility charging stations"
LANGUAGES CXX C
)
find_package(everest-cmake 0.5
COMPONENTS bundling
PATHS ../everest-cmake
NO_DEFAULT_PATH
)
find_package(everest-cmake 0.5
COMPONENTS bundling
)
if (NOT everest-cmake_FOUND)
message(STATUS "Retrieving everest-cmake using FetchContent")
include(FetchContent)
FetchContent_Declare(
everest-cmake
GIT_REPOSITORY https://github.com/EVerest/everest-cmake.git
GIT_TAG main
)
FetchContent_MakeAvailable(everest-cmake)
set(everest-cmake_DIR "${everest-cmake_SOURCE_DIR}")
set(everest-cmake_FIND_COMPONENTS "bundling")
include("${everest-cmake_SOURCE_DIR}/everest-cmake-config.cmake")
endif()
# make own cmake modules available
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# test whether linking with libatomic is required on some platforms
include(CheckAtomic)
if(HAVE_CXX_ATOMICS_WITH_LIB OR HAVE_CXX_ATOMICS64_WITH_LIB)
set(ATOMIC_LIBS "atomic")
else()
set(ATOMIC_LIBS "")
endif()
# test whether we are building on a musl based system.
# On these we need to increase the stack size as it is very low by default
option(USING_MUSL "Using musl" OFF)
find_program(LDD_EXECUTABLE ldd)
find_program(LS_EXECUTABLE ls)
if(LDD_EXECUTABLE AND LS_EXECUTABLE)
# we use ls as a testing executable as it is very likely to exist on any system. ldd prints the dynamic linked libraries
# of the given executable, if musl is used it will print it as a linked object
execute_process(COMMAND ${LDD_EXECUTABLE} ${LS_EXECUTABLE} OUTPUT_VARIABLE LDD_LS_OUTPUT ERROR_QUIET)
if(LDD_LS_OUTPUT MATCHES "musl")
message(STATUS "MUSL detected, increasing stack size to 8MiB")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,stack-size=8388608")
set(USING_MUSL ON)
endif()
else()
if(NOT LDD_EXECUTABLE)
message(WARNING "ldd not found, skipping musl detection")
endif()
if(NOT LS_EXECUTABLE)
message(WARNING "ls not found, skipping musl detection")
endif()
endif()
option(CREATE_SYMLINKS "Create symlinks to javascript modules and auxillary files - for development purposes" OFF)
option(CMAKE_RUN_CLANG_TIDY "Run clang-tidy" OFF)
option(ISO15118_2_GENERATE_AND_INSTALL_CERTIFICATES "Automatically generate and install certificates for development purposes" ON)
option(EVEREST_ENABLE_RUN_SCRIPT_GENERATION "Enables the generation of run scripts (convenience scripts for starting available configurations)" ON)
option(EVEREST_BUILD_DOCS "Build EVerest documentation" OFF)
option(EVEREST_SKIP_BUILD_API_DOC "Skip building the async API html doc for the EVerest API" OFF)
option(${PROJECT_NAME}_BUILD_TESTING "Build unit tests, used if included as dependency" OFF)
option(BUILD_TESTING "Build unit tests, used if standalone project" OFF)
option(EVEREST_ENABLE_COMPILE_WARNINGS "Enable compile warnings set in the EVEREST_COMPILE_OPTIONS flag" OFF)
option(EVEREST_ENABLE_GLOBAL_COMPILE_WARNINGS "Enable compile warnings set in the EVEREST_COMPILE_OPTIONS flag globally" OFF)
option(EVEREST_ENABLE_DEBUG_BUILD "Enable debug build" OFF)
option(EVEREST_BUILD_APPLICATIONS "Build applications like drivers for the EVerest stable API" ON)
option(EVEREST_LIBS_ONLY "Only build libraries, skip modules/applications/config" OFF)
option(EVEREST_IO_WITH_MQTT "Build MQTT (mosquitto) support in everest::io" ON)
set(EVEREST_INCLUDE_LIBS "" CACHE STRING "Semicolon-separated allowlist of libraries to build (empty = all)")
set(EVEREST_EXCLUDE_LIBS "" CACHE STRING "Semicolon-separated blocklist of libraries to skip")
# list of compile options that are passed to modules if EVEREST_ENABLE_COMPILE_WARNINGS=ON
# generated code has functions often not used
set(EVEREST_COMPILE_OPTIONS "-Wall;-Wno-unused-function" CACHE STRING "A list of compile options used for building modules")
if(EVEREST_ENABLE_GLOBAL_COMPILE_WARNINGS)
add_compile_options(${EVEREST_COMPILE_OPTIONS})
endif()
if((${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME} OR ${PROJECT_NAME}_BUILD_TESTING) AND BUILD_TESTING)
set(EVEREST_CORE_BUILD_TESTING ON)
set(everest-framework_BUILD_TESTING ON)
set(ISO15118_BUILD_TESTING ON)
set(ocpp_BUILD_TESTING ON)
set(LIBEVSE_SECURITY_BUILD_TESTING ON)
set(everest-evse_security_BUILD_TESTING ON)
set(everest-sqlite_BUILD_TESTING ON)
set(everest-timer_BUILD_TESTING ON)
set(everest-log_BUILD_TESTING ON)
set(IEEE2030_BUILD_TESTING ON)
endif()
# This is a flag for building development tests, but not necessarily to run them, for expample in case
# tests requires hardware.
option(BUILD_DEV_TESTS "Build dev tests" OFF)
ev_setup_cmake_variables_python_wheel()
option(${PROJECT_NAME}_INSTALL_EV_CLI_IN_PYTHON_VENV "Install ev-cli in python venv instead of using system" ON)
set(${PROJECT_NAME}_PYTHON_VENV_PATH "${CMAKE_BINARY_DIR}/venv" CACHE PATH "Path to python venv")
ev_setup_python_executable(
USE_PYTHON_VENV ${${PROJECT_NAME}_USE_PYTHON_VENV}
PYTHON_VENV_PATH ${${PROJECT_NAME}_PYTHON_VENV_PATH}
)
# Already include CTest here to allow it to find tests defined in subdirectories like lib and modules
if(EVEREST_CORE_BUILD_TESTING)
include(CTest)
if (NOT CMAKE_BUILD_TYPE)
if(EVEREST_ENABLE_DEBUG_BUILD)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
else()
set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "Build type" FORCE)
endif()
endif()
endif()
include(ev-define-dependency)
include(ev-lib-dependencies)
if(NOT EVEREST_LIBS_ONLY)
include("module-dependencies.cmake")
endif()
# this policy allows us to continue using the removed FindBoost module for now
if(POLICY CMP0167)
cmake_policy(SET CMP0167 OLD)
endif()
find_package(Boost
COMPONENTS
filesystem
program_options
thread
REQUIRED
)
include(compatibility/boost)
set(everest-framework_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/everest/framework)
set(everest-framework_BINARY_DIR ${CMAKE_BINARY_DIR}/lib/everest/framework)
set(EVEREST_SCHEMA_DIR ${everest-framework_SOURCE_DIR}/schemas)
set(everest-sqlite_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/everest/sqlite)
if(NOT DEFINED EVEREST_ENABLE_PY_SUPPORT)
set(EVEREST_ENABLE_PY_SUPPORT ON CACHE BOOL "Enable EVerest Python support" FORCE)
endif()
if(NOT DISABLE_EDM)
# FIXME (aw): this implicit definition for child projects is hacky
set(THIRD_PARTY_APP_DST "${CMAKE_INSTALL_LIBEXECDIR}/everest/3rd_party")
# edm
add_subdirectory(applications/dependency_manager)
evc_setup_edm()
if(EVEREST_DEPENDENCY_ENABLED_SDBUS_CPP AND NOT TARGET SDBusCpp::sdbus-c++)
add_library(SDBusCpp::sdbus-c++ ALIAS sdbus-c++)
endif()
# ev-cli and everest-testing
if(NOT EVEREST_LIBS_ONLY)
add_subdirectory(applications/utils)
endif()
else()
if(NOT EVEREST_LIBS_ONLY)
find_package(PalSigslot REQUIRED)
find_package(libnfc-nci REQUIRED)
find_package(pugixml REQUIRED)
find_package(CURL 7.84.0 REQUIRED)
find_package(ftxui 6 QUIET) # optional, if not available BringUp module will not be built
find_package(sdbus-c++ REQUIRED)
endif()
find_package(ryml REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(fmt REQUIRED)
find_package(date REQUIRED)
find_package(OpenSSL 3 REQUIRED)
endif()
if(EVEREST_BUILD_DOCS)
add_subdirectory(docs)
endif()
if(EVEREST_LIBS_ONLY)
# ev-targets provides ev_register_library_target() used by some libraries
include(ev-targets)
else()
include(ev-project-bootstrap)
ev_add_project()
# create MF_ROOT_CA if not available
if (ISO15118_2_GENERATE_AND_INSTALL_CERTIFICATES)
file(TOUCH config/certs/ca/mf/MF_ROOT_CA.pem)
endif()
endif()
add_subdirectory(lib)
if(NOT EVEREST_LIBS_ONLY)
# config
# FIXME (aw): this should be optional
add_subdirectory(config)
if(EVEREST_BUILD_APPLICATIONS)
add_subdirectory(applications)
endif()
ev_install_project()
endif()
# configure clang-tidy if requested
if(CMAKE_RUN_CLANG_TIDY)
message("Enabling clang-tidy")
set(CMAKE_CXX_CLANG_TIDY clang-tidy)
endif()
# build doxygen documentation if doxygen is available
find_package(Doxygen)
if(DOXYGEN_FOUND)
set( DOXYGEN_OUTPUT_DIRECTORY dist/docs )
doxygen_add_docs(doxygen-${PROJECT_NAME} everest.js include lib src)
endif()
# testing
if(EVEREST_CORE_BUILD_TESTING AND NOT EVEREST_LIBS_ONLY)
add_subdirectory(tests)
else()
message("Not running unit tests")
endif()
if(NOT EVEREST_LIBS_ONLY)
# convenience target for integration tests
add_custom_target(install_everest_testing
COMMAND
if [ -z "${CPM_PACKAGE_everest-utils_SOURCE_DIR}" ] \;
then echo "Could not determine location of everest-utils, please install everest-testing manually!" \;
else echo "Found everest-utils at ${CPM_PACKAGE_everest-utils_SOURCE_DIR}" \;
${Python3_EXECUTABLE} -m pip install -e "${CPM_PACKAGE_everest-utils_SOURCE_DIR}/everest-testing" \;
fi\;
DEPENDS
everestpy_pip_install_dist
COMMENT
"Installing dependencies for testing EVerest"
)
endif()

View File

@@ -0,0 +1,3 @@
# Code of Conduct
EVerest follows the [Linux Foundation Projects Code of Conduct](https://lfprojects.org/policies/code-of-conduct/).

View File

@@ -0,0 +1,3 @@
# Contributing to EVerest
Please refer to our comprehensive [Contributing Guide](https://everest.github.io/nightly/project/contributing.html).

View File

@@ -0,0 +1,4 @@
# Governance
Please refer to our [EVerest project governance policy](https://everest.github.io/nightly/project/governance/governance.html).

201
tools/EVerest-main/LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,235 @@
module(
name = "everest-core",
version = "2026.02.0",
)
###############################################################################
# B A Z E L C E N T R A L R E G I S T R Y # https://registry.bazel.build/
###############################################################################
bazel_dep(name = "bazel_features", version = "1.21.0")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
BOOST_VERSION = "1.87.0"
bazel_dep(name = "boost.asio", version = BOOST_VERSION)
bazel_dep(name = "boost.process", version = BOOST_VERSION)
bazel_dep(name = "boost.uuid", version = BOOST_VERSION)
bazel_dep(name = "boost.exception", version = BOOST_VERSION)
bazel_dep(name = "boost.log", version = BOOST_VERSION)
bazel_dep(name = "boost.utility", version = BOOST_VERSION)
bazel_dep(name = "boost.program_options", version = BOOST_VERSION)
bazel_dep(name = "catch2", version = "3.13.0")
bazel_dep(name = "cxx.rs", version = "1.0.194")
bazel_dep(name = "fast_float", version = "6.1.6")
bazel_dep(name = "fmt", version = "12.1.0", repo_name = "com_github_fmtlib_fmt")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "nlohmann_json", version = "3.12.0.bcr.1", repo_name = "com_github_nlohmann_json")
bazel_dep(name = "libcap", version = "2.27.bcr.1")
bazel_dep(name = "libevent", version = "2.1.12-stable.bcr.0")
bazel_dep(name = "openssl", version = "3.3.1.bcr.9")
bazel_dep(name = "platforms", version = "0.0.11")
bazel_dep(name = "pugixml", version = "1.15")
bazel_dep(name = "rapidyaml", version = "0.10.0")
bazel_dep(name = "rules_cc", version = "0.2.14")
bazel_dep(name = "rules_foreign_cc", version = "0.14.0")
bazel_dep(name = "rules_license", version = "0.0.7")
bazel_dep(name = "rules_python", version = "1.3.0")
bazel_dep(name = "rules_rust", version = "0.67.0")
bazel_dep(name = "rules_shell", version = "0.6.1")
bazel_dep(name = "pybind11_bazel", version = "3.0.0")
bazel_dep(name = "sqlite3", version = "3.51.2")
single_version_override(
module_name = "boost.context",
patches = ["//third-party/bazel/patches:boost.context.patch"],
)
###############################################################################
# C R O S S - C O M P I L A T I O N T O O L C H A I N S
###############################################################################
# Module extension for cross-compilation toolchain repositories
cross_toolchains = use_extension("//third-party/bazel/toolchains:extensions.bzl", "toolchains")
use_repo(
cross_toolchains,
"cc_toolchain_aarch64-linux-gnu-x86_64-linux",
"cc_toolchain_aarch64-linux-musl-x86_64-linux",
"cc_toolchain_armv7-linux-gnu-x86_64-linux",
"cc_toolchain_armv7-linux-musl-x86_64-linux",
)
register_toolchains("@cc_toolchain_aarch64-linux-gnu-x86_64-linux//:aarch64-linux-glibc_toolchain_toolchain")
register_toolchains("@cc_toolchain_aarch64-linux-musl-x86_64-linux//:aarch64-linux-musl_toolchain_toolchain")
register_toolchains("@cc_toolchain_armv7-linux-musl-x86_64-linux//:armv7-linux-musl_toolchain_toolchain")
register_toolchains("@cc_toolchain_armv7-linux-gnu-x86_64-linux//:armv7-linux-gnu_toolchain_toolchain")
###############################################################################
# N O N M O D U L E
###############################################################################
deps = use_extension("//third-party/bazel:extension.bzl", "deps")
use_repo(
deps,
"com_github_HowardHinnant_date",
"com_github_pboettch_json-schema-validator",
"com_github_warmcatt_libwebsockets",
"mosquitto",
"openssl_source",
"pybind11_json",
"sigslot",
)
###############################################################################
# P Y T H O N
###############################################################################
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
is_default = True,
python_version = "3.10",
)
use_repo(python, "python_3_10")
pip_deps = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip_deps.parse(
hub_name = "everest-testing_pip_deps",
python_version = "3.10",
requirements_lock = "//applications/utils:requirements-bazel.txt",
)
use_repo(pip_deps, "everest-testing_pip_deps")
###############################################################################
# R U S T
###############################################################################
RUST_EDITION = "2021"
RUST_VERSION = "1.86.0"
RUST_EXTRA_TARGET_TRIPLES = {
"armv7-unknown-linux-musleabihf": [
"@platforms//os:linux",
"@platforms//cpu:armv7",
"@everest-core//third-party/bazel/toolchains:musl",
],
"armv7-unknown-linux-gnueabihf": [
"@platforms//os:linux",
"@platforms//cpu:armv7",
"@everest-core//third-party/bazel/toolchains:gnu",
],
"aarch64-unknown-linux-musl": [
"@platforms//os:linux",
"@platforms//cpu:aarch64",
"@everest-core//third-party/bazel/toolchains:musl",
],
"aarch64-unknown-linux-gnu": [
"@platforms//os:linux",
"@platforms//cpu:aarch64",
"@everest-core//third-party/bazel/toolchains:gnu",
],
}
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
rust.toolchain(
edition = RUST_EDITION,
versions = [
RUST_VERSION,
"nightly/2026-02-09",
],
)
[
rust.repository_set(
name = "rust_{}_repository_set_x86_64-unknown-linux-gnu".format(target_triple.replace("-", "_")),
edition = RUST_EDITION,
exec_triple = "x86_64-unknown-linux-gnu",
target_compatible_with = target_compatible_with,
target_triple = target_triple,
versions = [RUST_VERSION],
)
for (target_triple, target_compatible_with) in RUST_EXTRA_TARGET_TRIPLES.items()
]
use_repo(rust, "rust_toolchains")
register_toolchains("@rust_toolchains//:all")
everest_crate_index = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")
everest_crate_index.from_cargo(
name = "everest_crate_index",
cargo_lockfile = "@everest-core//modules:Cargo.lock",
manifests = [
"@everest-core//modules:Cargo.toml",
"@everest-core//modules/HardwareDrivers/PowerMeters/RsIskraMeter:Cargo.toml",
"@everest-core//modules/HardwareDrivers/Payment/RsPaymentTerminal:Cargo.toml",
"@everest-core//modules/Examples/RustExamples/RsExample:Cargo.toml",
"@everest-core//modules/Examples/RustExamples/RsExampleUser:Cargo.toml",
],
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"armv7-unknown-linux-gnueabihf",
],
)
everest_crate_index.annotation(
crate = "everestrs",
crate_features = ["build_bazel"],
repositories = ["everest_crate_index"],
deps = [
"//lib/everest/framework/everestrs/everestrs",
"//lib/everest/framework/everestrs/everestrs-build",
],
)
use_repo(
everest_crate_index,
"everest_crate_index",
)
###############################################################################
# F R A M E W O R K C R A T E I N D I C E S
###############################################################################
everest_framework_crate_index = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")
everest_framework_crate_index.from_cargo(
name = "everest_framework_crate_index",
cargo_lockfile = "//lib/everest/framework/everestrs:Cargo.lock",
manifests = [
"//lib/everest/framework/everestrs:Cargo.toml",
"//lib/everest/framework/everestrs/everestrs:Cargo.toml",
"//lib/everest/framework/everestrs/everestrs-build:Cargo.toml",
"//lib/everest/framework/everestrs/everestrs-derive:Cargo.toml",
],
supported_platform_triples = [
"x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
"armv7-unknown-linux-gnueabihf",
],
)
everest_crate_index.annotation(
crate = "log",
override_target_lib = "@everest_framework_crate_index//:log",
repositories = ["everest_crate_index"],
)
use_repo(
everest_framework_crate_index,
"everest_framework_crate_index",
)
everest_framework_validate_crate_index = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")
everest_framework_validate_crate_index.from_cargo(
name = "everest_framework_validate_crate_index",
cargo_lockfile = "//lib/everest/framework/bazel/validate:Cargo.lock",
manifests = [
"//lib/everest/framework/bazel/validate:Cargo.toml",
],
)
use_repo(
everest_framework_validate_crate_index,
"everest_framework_validate_crate_index",
)

View File

@@ -0,0 +1,65 @@
![Alt text](docs/images/everest_horizontal-color.svg)
[![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/6739/badge)](https://bestpractices.coreinfrastructure.org/projects/6739)
# EVerest
EVerest is a [Linux Foundation Energy](https://lfenergy.org/) backed open-source modular
framework for setting up a full stack environment for EV charging. The modular
software architecture fosters customizablility and lets you configure your
dedicated charging station based on interchangeable modules. All communication
is performed by the lightweight and flexible MQTT message queueing service.
EVerest will help to speed the adoption to e-mobility by utilizing all the
open-source advantages for the EV charging world. It will also enable new
features for local energy management, PV-integration and many more!
# Main Features
- Modular and extensible architecture
- Support for AC and DC charging
- Support for EV charging protocols
- OCPP 1.6, OCPP 2.0.1 and OCPP 2.1
- ISO 15118-2, -3 and -20
- IEC 61851
- DIN SPEC 70121
- Ready-to-use hardware drivers for many compatible hardware components
- BSPs for charge controllers
- Powermeters
- Isolation monitors
- DC Power supplies
- RFID/NFC readers
- Payment terminals
- Energy management implementations and API
- Standardized and stable APIs to allow easy integrations
- Bring-up modules for custom hardware testing and integration
- Ensured standards compliance
- OTA service to keep EV chargers up-to-date
- Security best practices following OpenSSF
- ISO / IEC 5230 open source license compliance
- Secure communication channels through TPM
- Yocto support for custom embedded Linux images
# Getting Started
See our documentation on [everest.github.io](https://everest.github.io) to get started with using or developing EVerest. The best
way to start is with our [Getting Started Guides](https://everest.github.io/nightly/how-to-guides/getting-started/index.html).
# Community
Find more information about the community and ways to get involved [here](https://everest.github.io/nightly/project/community.html).
# Contributing to EVerest
Anyone can contribute to EVerest! Learn more about getting involved
[here](https://everest.github.io/nightly/project/contributing.html).
# License
EVerest is licensed under the Apache License, Version 2.0.
See [LICENSE](LICENSE) for the full license text.
# Background
The EVerest project was initiated by PIONIX GmbH to help with the
electrification of the mobility sector.

View File

@@ -0,0 +1,4 @@
# Releases
Please refer to our [Release Documentation](https://everest.github.io/nightly/project/releases/releases-and-versioning.html).

View File

@@ -0,0 +1,3 @@
# Security Policy
Please refer to our [EVerest Security Policy](https://everest.github.io/nightly/project/governance/security.html).

View File

@@ -0,0 +1,5 @@
# Third-party dependencies used by this project
- [CodeCoverage.cmake](https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake) licensed under [The 3-Clause BSD License](https://opensource.org/licenses/BSD-3-Clause)
- [Nanopb - Protocol Buffers for Embedded Systems](https://github.com/nanopb/nanopb) licensed under [The zlib License](https://opensource.org/licenses/Zlib)
- [sphinx-notfound-page](https://github.com/readthedocs/sphinx-notfound-page) licensed under [The MIT License](https://opensource.org/licenses/MIT)

View File

@@ -0,0 +1 @@
add_subdirectory(pionix_chargebridge)

View File

@@ -0,0 +1,31 @@
# Applications related to EVerest
This directory will contain applications that are used during the EVerest build process,
deployed to the target hardware and useful scripts for development.
## containers/
containers/ contains multiple useful Dockerfile based containers that can be used for development.
* `mosquittto` : An mqtt broker
* `mqtt-explorer` : Tool to inspect messages on mqtt topics
* `nodered` : NodeRED application to run i.e. sil flows
* `steve` : OCPP backend
## dependency_manager
Contains the EDM (EVerest Dependency Manager) tool which is a cli to
manage an EVerest workspace and external dependencies during building.
## everest_dev_tool
A CLI designed to provide helpful commands/aliases for developing.
## utils/
utils/ contains ev-cli and everest-testing as well as additional code integrated from everest-utils.
This might get broken up further in the future.
## devrd
Script to manage devcontainer + containerized services in the development environment

View File

@@ -0,0 +1,15 @@
FROM eclipse-mosquitto:2.0.22
COPY mosquitto.conf /mosquitto/config/mosquitto.conf
ARG TARGETARCH
COPY entrypoint_wrapper.sh /entrypoint_wrapper.sh
RUN if [ ${TARGETARCH} != "amd64" ]; then \
mv /docker-entrypoint.sh /wrapped_entrypoint.sh; \
cp /entrypoint_wrapper.sh /entrypoint.sh; \
else \
mv /docker-entrypoint.sh /entrypoint.sh; \
fi; \
rm /entrypoint_wrapper.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"]

View File

@@ -0,0 +1,37 @@
#!/bin/ash
# ---------------------------------------------
# Architecture Warning Wrapper Script
#
# This script is used as an entrypoint wrapper to emit a warning
# when the container is not running on the officially supported
# amd64 (x86_64) architecture.
#
# It checks for the presence of a wrapped entrypoint script
# (/wrapped_entrypoint.sh) and executes it if found; otherwise,
# it falls back to executing the provided command directly.
#
# The warning is shown both before and after the wrapped command
# to ensure visibility.
# ---------------------------------------------
function print_warning {
echo -e "\033[0;31m"
echo "-------------------------------------------------------------"
echo "⚠️ WARNING: Unsupported Architecture Detected"
echo
echo "This Docker image is not running on the amd64 (x86_64) architecture."
echo "It is recommended to use the amd64-based image for full compatibility."
echo "Other architectures are not officially supported and may cause issues."
echo
echo "-------------------------------------------------------------"
echo -e "\033[0m"
}
print_warning
if [ -f /wrapped_entrypoint.sh ]; then
exec /wrapped_entrypoint.sh "$@"
else
exec "$@"
fi

View File

@@ -0,0 +1,876 @@
# Config file for mosquitto
#
# See mosquitto.conf(5) for more information.
#
# Default values are shown, uncomment to change.
#
# Use the # character to indicate a comment, but only if it is the
# very first character on the line.
# =================================================================
# General configuration
# =================================================================
# Use per listener security settings.
#
# It is recommended this option be set before any other options.
#
# If this option is set to true, then all authentication and access control
# options are controlled on a per listener basis. The following options are
# affected:
#
# password_file acl_file psk_file auth_plugin auth_opt_* allow_anonymous
# auto_id_prefix allow_zero_length_clientid
#
# Note that if set to true, then a durable client (i.e. with clean session set
# to false) that has disconnected will use the ACL settings defined for the
# listener that it was most recently connected to.
#
# The default behaviour is for this to be set to false, which maintains the
# setting behaviour from previous versions of mosquitto.
#per_listener_settings false
# This option controls whether a client is allowed to connect with a zero
# length client id or not. This option only affects clients using MQTT v3.1.1
# and later. If set to false, clients connecting with a zero length client id
# are disconnected. If set to true, clients will be allocated a client id by
# the broker. This means it is only useful for clients with clean session set
# to true.
#allow_zero_length_clientid true
# If allow_zero_length_clientid is true, this option allows you to set a prefix
# to automatically generated client ids to aid visibility in logs.
# Defaults to 'auto-'
#auto_id_prefix auto-
# This option affects the scenario when a client subscribes to a topic that has
# retained messages. It is possible that the client that published the retained
# message to the topic had access at the time they published, but that access
# has been subsequently removed. If check_retain_source is set to true, the
# default, the source of a retained message will be checked for access rights
# before it is republished. When set to false, no check will be made and the
# retained message will always be published. This affects all listeners.
#check_retain_source true
# QoS 1 and 2 messages will be allowed inflight per client until this limit
# is exceeded. Defaults to 0. (No maximum)
# See also max_inflight_messages
#max_inflight_bytes 0
# The maximum number of QoS 1 and 2 messages currently inflight per
# client.
# This includes messages that are partway through handshakes and
# those that are being retried. Defaults to 20. Set to 0 for no
# maximum. Setting to 1 will guarantee in-order delivery of QoS 1
# and 2 messages.
#max_inflight_messages 20
# For MQTT v5 clients, it is possible to have the server send a "server
# keepalive" value that will override the keepalive value set by the client.
# This is intended to be used as a mechanism to say that the server will
# disconnect the client earlier than it anticipated, and that the client should
# use the new keepalive value. The max_keepalive option allows you to specify
# that clients may only connect with keepalive less than or equal to this
# value, otherwise they will be sent a server keepalive telling them to use
# max_keepalive. This only applies to MQTT v5 clients. The maximum value
# allowable is 65535. Do not set below 10.
#max_keepalive 65535
# For MQTT v5 clients, it is possible to have the server send a "maximum packet
# size" value that will instruct the client it will not accept MQTT packets
# with size greater than max_packet_size bytes. This applies to the full MQTT
# packet, not just the payload. Setting this option to a positive value will
# set the maximum packet size to that number of bytes. If a client sends a
# packet which is larger than this value, it will be disconnected. This applies
# to all clients regardless of the protocol version they are using, but v3.1.1
# and earlier clients will of course not have received the maximum packet size
# information. Defaults to no limit. Setting below 20 bytes is forbidden
# because it is likely to interfere with ordinary client operation, even with
# very small payloads.
#max_packet_size 0
# QoS 1 and 2 messages above those currently in-flight will be queued per
# client until this limit is exceeded. Defaults to 0. (No maximum)
# See also max_queued_messages.
# If both max_queued_messages and max_queued_bytes are specified, packets will
# be queued until the first limit is reached.
#max_queued_bytes 0
# Set the maximum QoS supported. Clients publishing at a QoS higher than
# specified here will be disconnected.
#max_qos 2
# The maximum number of QoS 1 and 2 messages to hold in a queue per client
# above those that are currently in-flight. Defaults to 1000. Set
# to 0 for no maximum (not recommended).
# See also queue_qos0_messages.
# See also max_queued_bytes.
#max_queued_messages 1000
#
# This option sets the maximum number of heap memory bytes that the broker will
# allocate, and hence sets a hard limit on memory use by the broker. Memory
# requests that exceed this value will be denied. The effect will vary
# depending on what has been denied. If an incoming message is being processed,
# then the message will be dropped and the publishing client will be
# disconnected. If an outgoing message is being sent, then the individual
# message will be dropped and the receiving client will be disconnected.
# Defaults to no limit.
#memory_limit 0
# This option sets the maximum publish payload size that the broker will allow.
# Received messages that exceed this size will not be accepted by the broker.
# The default value is 0, which means that all valid MQTT messages are
# accepted. MQTT imposes a maximum payload size of 268435455 bytes.
#message_size_limit 0
# This option allows persistent clients (those with clean session set to false)
# to be removed if they do not reconnect within a certain time frame.
#
# This is a non-standard option in MQTT V3.1 but allowed in MQTT v3.1.1.
#
# Badly designed clients may set clean session to false whilst using a randomly
# generated client id. This leads to persistent clients that will never
# reconnect. This option allows these clients to be removed.
#
# The expiration period should be an integer followed by one of h d w m y for
# hour, day, week, month and year respectively. For example
#
# persistent_client_expiration 2m
# persistent_client_expiration 14d
# persistent_client_expiration 1y
#
# The default if not set is to never expire persistent clients.
#persistent_client_expiration
# Write process id to a file. Default is a blank string which means
# a pid file shouldn't be written.
# This should be set to /var/run/mosquitto/mosquitto.pid if mosquitto is
# being run automatically on boot with an init script and
# start-stop-daemon or similar.
#pid_file
# Set to true to queue messages with QoS 0 when a persistent client is
# disconnected. These messages are included in the limit imposed by
# max_queued_messages and max_queued_bytes
# Defaults to false.
# This is a non-standard option for the MQTT v3.1 spec but is allowed in
# v3.1.1.
#queue_qos0_messages false
# Set to false to disable retained message support. If a client publishes a
# message with the retain bit set, it will be disconnected if this is set to
# false.
#retain_available true
# Disable Nagle's algorithm on client sockets. This has the effect of reducing
# latency of individual messages at the potential cost of increasing the number
# of packets being sent.
#set_tcp_nodelay false
# Time in seconds between updates of the $SYS tree.
# Set to 0 to disable the publishing of the $SYS tree.
#sys_interval 10
# The MQTT specification requires that the QoS of a message delivered to a
# subscriber is never upgraded to match the QoS of the subscription. Enabling
# this option changes this behaviour. If upgrade_outgoing_qos is set true,
# messages sent to a subscriber will always match the QoS of its subscription.
# This is a non-standard option explicitly disallowed by the spec.
#upgrade_outgoing_qos false
# When run as root, drop privileges to this user and its primary
# group.
# Set to root to stay as root, but this is not recommended.
# If set to "mosquitto", or left unset, and the "mosquitto" user does not exist
# then it will drop privileges to the "nobody" user instead.
# If run as a non-root user, this setting has no effect.
# Note that on Windows this has no effect and so mosquitto should be started by
# the user you wish it to run as.
#user mosquitto
# =================================================================
# Listeners
# =================================================================
# Listen on a port/ip address combination. By using this variable
# multiple times, mosquitto can listen on more than one port. If
# this variable is used and neither bind_address nor port given,
# then the default listener will not be started.
# The port number to listen on must be given. Optionally, an ip
# address or host name may be supplied as a second argument. In
# this case, mosquitto will attempt to bind the listener to that
# address and so restrict access to the associated network and
# interface. By default, mosquitto will listen on all interfaces.
# Note that for a websockets listener it is not possible to bind to a host
# name.
#
# On systems that support Unix Domain Sockets, it is also possible
# to create a # Unix socket rather than opening a TCP socket. In
# this case, the port number should be set to 0 and a unix socket
# path must be provided, e.g.
# listener 0 /tmp/mosquitto.sock
#
# listener port-number [ip address/host name/unix socket path]
listener 1883
# By default, a listener will attempt to listen on all supported IP protocol
# versions. If you do not have an IPv4 or IPv6 interface you may wish to
# disable support for either of those protocol versions. In particular, note
# that due to the limitations of the websockets library, it will only ever
# attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail
# if IPv6 is not available.
#
# Set to `ipv4` to force the listener to only use IPv4, or set to `ipv6` to
# force the listener to only use IPv6. If you want support for both IPv4 and
# IPv6, then do not use the socket_domain option.
#
#socket_domain
# Bind the listener to a specific interface. This is similar to
# the [ip address/host name] part of the listener definition, but is useful
# when an interface has multiple addresses or the address may change. If used
# with the [ip address/host name] part of the listener definition, then the
# bind_interface option will take priority.
# Not available on Windows.
#
# Example: bind_interface eth0
#bind_interface
# When a listener is using the websockets protocol, it is possible to serve
# http data as well. Set http_dir to a directory which contains the files you
# wish to serve. If this option is not specified, then no normal http
# connections will be possible.
#http_dir
# The maximum number of client connections to allow. This is
# a per listener setting.
# Default is -1, which means unlimited connections.
# Note that other process limits mean that unlimited connections
# are not really possible. Typically the default maximum number of
# connections possible is around 1024.
#max_connections -1
# The listener can be restricted to operating within a topic hierarchy using
# the mount_point option. This is achieved be prefixing the mount_point string
# to all topics for any clients connected to this listener. This prefixing only
# happens internally to the broker; the client will not see the prefix.
#mount_point
# Choose the protocol to use when listening.
# This can be either mqtt or websockets.
# Certificate based TLS may be used with websockets, except that only the
# cafile, certfile, keyfile, ciphers, and ciphers_tls13 options are supported.
#protocol mqtt
# Set use_username_as_clientid to true to replace the clientid that a client
# connected with with its username. This allows authentication to be tied to
# the clientid, which means that it is possible to prevent one client
# disconnecting another by using the same clientid.
# If a client connects with no username it will be disconnected as not
# authorised when this option is set to true.
# Do not use in conjunction with clientid_prefixes.
# See also use_identity_as_username.
#use_username_as_clientid
# Change the websockets headers size. This is a global option, it is not
# possible to set per listener. This option sets the size of the buffer used in
# the libwebsockets library when reading HTTP headers. If you are passing large
# header data such as cookies then you may need to increase this value. If left
# unset, or set to 0, then the default of 1024 bytes will be used.
#websockets_headers_size
# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
# The following options can be used to enable certificate based SSL/TLS support
# for this listener. Note that the recommended port for MQTT over TLS is 8883,
# but this must be set manually.
#
# See also the mosquitto-tls man page and the "Pre-shared-key based SSL/TLS
# support" section. Only one of certificate or PSK encryption support can be
# enabled for any listener.
# Both of certfile and keyfile must be defined to enable certificate based
# TLS encryption.
# Path to the PEM encoded server certificate.
#certfile
# Path to the PEM encoded keyfile.
#keyfile
# If you wish to control which encryption ciphers are used, use the ciphers
# option. The list of available ciphers can be optained using the "openssl
# ciphers" command and should be provided in the same format as the output of
# that command. This applies to TLS 1.2 and earlier versions only. Use
# ciphers_tls1.3 for TLS v1.3.
#ciphers
# Choose which TLS v1.3 ciphersuites are used for this listener.
# Defaults to "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
#ciphers_tls1.3
# If you have require_certificate set to true, you can create a certificate
# revocation list file to revoke access to particular client certificates. If
# you have done this, use crlfile to point to the PEM encoded revocation file.
#crlfile
# To allow the use of ephemeral DH key exchange, which provides forward
# security, the listener must load DH parameters. This can be specified with
# the dhparamfile option. The dhparamfile can be generated with the command
# e.g. "openssl dhparam -out dhparam.pem 2048"
#dhparamfile
# By default an TLS enabled listener will operate in a similar fashion to a
# https enabled web server, in that the server has a certificate signed by a CA
# and the client will verify that it is a trusted certificate. The overall aim
# is encryption of the network traffic. By setting require_certificate to true,
# the client must provide a valid certificate in order for the network
# connection to proceed. This allows access to the broker to be controlled
# outside of the mechanisms provided by MQTT.
#require_certificate false
# cafile and capath define methods of accessing the PEM encoded
# Certificate Authority certificates that will be considered trusted when
# checking incoming client certificates.
# cafile defines the path to a file containing the CA certificates.
# capath defines a directory that will be searched for files
# containing the CA certificates. For capath to work correctly, the
# certificate files must have ".crt" as the file ending and you must run
# "openssl rehash <path to capath>" each time you add/remove a certificate.
#cafile
#capath
# If require_certificate is true, you may set use_identity_as_username to true
# to use the CN value from the client certificate as a username. If this is
# true, the password_file option will not be used for this listener.
#use_identity_as_username false
# -----------------------------------------------------------------
# Pre-shared-key based SSL/TLS support
# -----------------------------------------------------------------
# The following options can be used to enable PSK based SSL/TLS support for
# this listener. Note that the recommended port for MQTT over TLS is 8883, but
# this must be set manually.
#
# See also the mosquitto-tls man page and the "Certificate based SSL/TLS
# support" section. Only one of certificate or PSK encryption support can be
# enabled for any listener.
# The psk_hint option enables pre-shared-key support for this listener and also
# acts as an identifier for this listener. The hint is sent to clients and may
# be used locally to aid authentication. The hint is a free form string that
# doesn't have much meaning in itself, so feel free to be creative.
# If this option is provided, see psk_file to define the pre-shared keys to be
# used or create a security plugin to handle them.
#psk_hint
# When using PSK, the encryption ciphers used will be chosen from the list of
# available PSK ciphers. If you want to control which ciphers are available,
# use the "ciphers" option. The list of available ciphers can be optained
# using the "openssl ciphers" command and should be provided in the same format
# as the output of that command.
#ciphers
# Set use_identity_as_username to have the psk identity sent by the client used
# as its username. Authentication will be carried out using the PSK rather than
# the MQTT username/password and so password_file will not be used for this
# listener.
#use_identity_as_username false
listener 9001
protocol websockets
# =================================================================
# Persistence
# =================================================================
# If persistence is enabled, save the in-memory database to disk
# every autosave_interval seconds. If set to 0, the persistence
# database will only be written when mosquitto exits. See also
# autosave_on_changes.
# Note that writing of the persistence database can be forced by
# sending mosquitto a SIGUSR1 signal.
#autosave_interval 1800
# If true, mosquitto will count the number of subscription changes, retained
# messages received and queued messages and if the total exceeds
# autosave_interval then the in-memory database will be saved to disk.
# If false, mosquitto will save the in-memory database to disk by treating
# autosave_interval as a time in seconds.
#autosave_on_changes false
# Save persistent message data to disk (true/false).
# This saves information about all messages, including
# subscriptions, currently in-flight messages and retained
# messages.
# retained_persistence is a synonym for this option.
#persistence false
# The filename to use for the persistent database, not including
# the path.
#persistence_file mosquitto.db
# Location for persistent database.
# Default is an empty string (current directory).
# Set to e.g. /var/lib/mosquitto if running as a proper service on Linux or
# similar.
#persistence_location
# =================================================================
# Logging
# =================================================================
# Places to log to. Use multiple log_dest lines for multiple
# logging destinations.
# Possible destinations are: stdout stderr syslog topic file dlt
#
# stdout and stderr log to the console on the named output.
#
# syslog uses the userspace syslog facility which usually ends up
# in /var/log/messages or similar.
#
# topic logs to the broker topic '$SYS/broker/log/<severity>',
# where severity is one of D, E, W, N, I, M which are debug, error,
# warning, notice, information and message. Message type severity is used by
# the subscribe/unsubscribe log_types and publishes log messages to
# $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe.
#
# The file destination requires an additional parameter which is the file to be
# logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be
# closed and reopened when the broker receives a HUP signal. Only a single file
# destination may be configured.
#
# The dlt destination is for the automotive `Diagnostic Log and Trace` tool.
# This requires that Mosquitto has been compiled with DLT support.
#
# Note that if the broker is running as a Windows service it will default to
# "log_dest none" and neither stdout nor stderr logging is available.
# Use "log_dest none" if you wish to disable logging.
#log_dest stderr
# Types of messages to log. Use multiple log_type lines for logging
# multiple types of messages.
# Possible types are: debug, error, warning, notice, information,
# none, subscribe, unsubscribe, websockets, all.
# Note that debug type messages are for decoding the incoming/outgoing
# network packets. They are not logged in "topics".
#log_type error
#log_type warning
#log_type notice
#log_type information
# If set to true, client connection and disconnection messages will be included
# in the log.
#connection_messages true
# If using syslog logging (not on Windows), messages will be logged to the
# "daemon" facility by default. Use the log_facility option to choose which of
# local0 to local7 to log to instead. The option value should be an integer
# value, e.g. "log_facility 5" to use local5.
#log_facility
# If set to true, add a timestamp value to each log message.
#log_timestamp true
# Set the format of the log timestamp. If left unset, this is the number of
# seconds since the Unix epoch.
# This is a free text string which will be passed to the strftime function. To
# get an ISO 8601 datetime, for example:
# log_timestamp_format %Y-%m-%dT%H:%M:%S
#log_timestamp_format
# Change the websockets logging level. This is a global option, it is not
# possible to set per listener. This is an integer that is interpreted by
# libwebsockets as a bit mask for its lws_log_levels enum. See the
# libwebsockets documentation for more details. "log_type websockets" must also
# be enabled.
#websockets_log_level 0
# =================================================================
# Security
# =================================================================
# If set, only clients that have a matching prefix on their
# clientid will be allowed to connect to the broker. By default,
# all clients may connect.
# For example, setting "secure-" here would mean a client "secure-
# client" could connect but another with clientid "mqtt" couldn't.
#clientid_prefixes
# Boolean value that determines whether clients that connect
# without providing a username are allowed to connect. If set to
# false then a password file should be created (see the
# password_file option) to control authenticated client access.
#
# Defaults to false, unless there are no listeners defined in the configuration
# file, in which case it is set to true, but connections are only allowed from
# the local machine.
allow_anonymous true
# -----------------------------------------------------------------
# Default authentication and topic access control
# -----------------------------------------------------------------
# Control access to the broker using a password file. This file can be
# generated using the mosquitto_passwd utility. If TLS support is not compiled
# into mosquitto (it is recommended that TLS support should be included) then
# plain text passwords are used, in which case the file should be a text file
# with lines in the format:
# username:password
# The password (and colon) may be omitted if desired, although this
# offers very little in the way of security.
#
# See the TLS client require_certificate and use_identity_as_username options
# for alternative authentication options. If an auth_plugin is used as well as
# password_file, the auth_plugin check will be made first.
#password_file
# Access may also be controlled using a pre-shared-key file. This requires
# TLS-PSK support and a listener configured to use it. The file should be text
# lines in the format:
# identity:key
# The key should be in hexadecimal format without a leading "0x".
# If an auth_plugin is used as well, the auth_plugin check will be made first.
#psk_file
# Control access to topics on the broker using an access control list
# file. If this parameter is defined then only the topics listed will
# have access.
# If the first character of a line of the ACL file is a # it is treated as a
# comment.
# Topic access is added with lines of the format:
#
# topic [read|write|readwrite|deny] <topic>
#
# The access type is controlled using "read", "write", "readwrite" or "deny".
# This parameter is optional (unless <topic> contains a space character) - if
# not given then the access is read/write. <topic> can contain the + or #
# wildcards as in subscriptions.
#
# The "deny" option can used to explicity deny access to a topic that would
# otherwise be granted by a broader read/write/readwrite statement. Any "deny"
# topics are handled before topics that grant read/write access.
#
# The first set of topics are applied to anonymous clients, assuming
# allow_anonymous is true. User specific topic ACLs are added after a
# user line as follows:
#
# user <username>
#
# The username referred to here is the same as in password_file. It is
# not the clientid.
#
#
# If is also possible to define ACLs based on pattern substitution within the
# topic. The patterns available for substition are:
#
# %c to match the client id of the client
# %u to match the username of the client
#
# The substitution pattern must be the only text for that level of hierarchy.
#
# The form is the same as for the topic keyword, but using pattern as the
# keyword.
# Pattern ACLs apply to all users even if the "user" keyword has previously
# been given.
#
# If using bridges with usernames and ACLs, connection messages can be allowed
# with the following pattern:
# pattern write $SYS/broker/connection/%c/state
#
# pattern [read|write|readwrite] <topic>
#
# Example:
#
# pattern write sensor/%u/data
#
# If an auth_plugin is used as well as acl_file, the auth_plugin check will be
# made first.
#acl_file
# -----------------------------------------------------------------
# External authentication and topic access plugin options
# -----------------------------------------------------------------
# External authentication and access control can be supported with the
# auth_plugin option. This is a path to a loadable plugin. See also the
# auth_opt_* options described below.
#
# The auth_plugin option can be specified multiple times to load multiple
# plugins. The plugins will be processed in the order that they are specified
# here. If the auth_plugin option is specified alongside either of
# password_file or acl_file then the plugin checks will be made first.
#
#auth_plugin
# If the auth_plugin option above is used, define options to pass to the
# plugin here as described by the plugin instructions. All options named
# using the format auth_opt_* will be passed to the plugin, for example:
#
# auth_opt_db_host
# auth_opt_db_port
# auth_opt_db_username
# auth_opt_db_password
# =================================================================
# Bridges
# =================================================================
# A bridge is a way of connecting multiple MQTT brokers together.
# Create a new bridge using the "connection" option as described below. Set
# options for the bridges using the remaining parameters. You must specify the
# address and at least one topic to subscribe to.
#
# Each connection must have a unique name.
#
# The address line may have multiple host address and ports specified. See
# below in the round_robin description for more details on bridge behaviour if
# multiple addresses are used. Note that if you use an IPv6 address, then you
# are required to specify a port.
#
# The direction that the topic will be shared can be chosen by
# specifying out, in or both, where the default value is out.
# The QoS level of the bridged communication can be specified with the next
# topic option. The default QoS level is 0, to change the QoS the topic
# direction must also be given.
#
# The local and remote prefix options allow a topic to be remapped when it is
# bridged to/from the remote broker. This provides the ability to place a topic
# tree in an appropriate location.
#
# For more details see the mosquitto.conf man page.
#
# Multiple topics can be specified per connection, but be careful
# not to create any loops.
#
# If you are using bridges with cleansession set to false (the default), then
# you may get unexpected behaviour from incoming topics if you change what
# topics you are subscribing to. This is because the remote broker keeps the
# subscription for the old topic. If you have this problem, connect your bridge
# with cleansession set to true, then reconnect with cleansession set to false
# as normal.
#connection <name>
#address <host>[:<port>] [<host>[:<port>]]
#topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix]
# If you need to have the bridge connect over a particular network interface,
# use bridge_bind_address to tell the bridge which local IP address the socket
# should bind to, e.g. `bridge_bind_address 192.168.1.10`
#bridge_bind_address
# If a bridge has topics that have "out" direction, the default behaviour is to
# send an unsubscribe request to the remote broker on that topic. This means
# that changing a topic direction from "in" to "out" will not keep receiving
# incoming messages. Sending these unsubscribe requests is not always
# desirable, setting bridge_attempt_unsubscribe to false will disable sending
# the unsubscribe request.
#bridge_attempt_unsubscribe true
# Set the version of the MQTT protocol to use with for this bridge. Can be one
# of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311.
#bridge_protocol_version mqttv311
# Set the clean session variable for this bridge.
# When set to true, when the bridge disconnects for any reason, all
# messages and subscriptions will be cleaned up on the remote
# broker. Note that with cleansession set to true, there may be a
# significant amount of retained messages sent when the bridge
# reconnects after losing its connection.
# When set to false, the subscriptions and messages are kept on the
# remote broker, and delivered when the bridge reconnects.
#cleansession false
# Set the amount of time a bridge using the lazy start type must be idle before
# it will be stopped. Defaults to 60 seconds.
#idle_timeout 60
# Set the keepalive interval for this bridge connection, in
# seconds.
#keepalive_interval 60
# Set the clientid to use on the local broker. If not defined, this defaults to
# 'local.<clientid>'. If you are bridging a broker to itself, it is important
# that local_clientid and clientid do not match.
#local_clientid
# If set to true, publish notification messages to the local and remote brokers
# giving information about the state of the bridge connection. Retained
# messages are published to the topic $SYS/broker/connection/<clientid>/state
# unless the notification_topic option is used.
# If the message is 1 then the connection is active, or 0 if the connection has
# failed.
# This uses the last will and testament feature.
#notifications true
# Choose the topic on which notification messages for this bridge are
# published. If not set, messages are published on the topic
# $SYS/broker/connection/<clientid>/state
#notification_topic
# Set the client id to use on the remote end of this bridge connection. If not
# defined, this defaults to 'name.hostname' where name is the connection name
# and hostname is the hostname of this computer.
# This replaces the old "clientid" option to avoid confusion. "clientid"
# remains valid for the time being.
#remote_clientid
# Set the password to use when connecting to a broker that requires
# authentication. This option is only used if remote_username is also set.
# This replaces the old "password" option to avoid confusion. "password"
# remains valid for the time being.
#remote_password
# Set the username to use when connecting to a broker that requires
# authentication.
# This replaces the old "username" option to avoid confusion. "username"
# remains valid for the time being.
#remote_username
# Set the amount of time a bridge using the automatic start type will wait
# until attempting to reconnect.
# This option can be configured to use a constant delay time in seconds, or to
# use a backoff mechanism based on "Decorrelated Jitter", which adds a degree
# of randomness to when the restart occurs.
#
# Set a constant timeout of 20 seconds:
# restart_timeout 20
#
# Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of
# 60 seconds:
# restart_timeout 10 30
#
# Defaults to jitter with a base of 5 and cap of 30
#restart_timeout 5 30
# If the bridge has more than one address given in the address/addresses
# configuration, the round_robin option defines the behaviour of the bridge on
# a failure of the bridge connection. If round_robin is false, the default
# value, then the first address is treated as the main bridge connection. If
# the connection fails, the other secondary addresses will be attempted in
# turn. Whilst connected to a secondary bridge, the bridge will periodically
# attempt to reconnect to the main bridge until successful.
# If round_robin is true, then all addresses are treated as equals. If a
# connection fails, the next address will be tried and if successful will
# remain connected until it fails
#round_robin false
# Set the start type of the bridge. This controls how the bridge starts and
# can be one of three types: automatic, lazy and once. Note that RSMB provides
# a fourth start type "manual" which isn't currently supported by mosquitto.
#
# "automatic" is the default start type and means that the bridge connection
# will be started automatically when the broker starts and also restarted
# after a short delay (30 seconds) if the connection fails.
#
# Bridges using the "lazy" start type will be started automatically when the
# number of queued messages exceeds the number set with the "threshold"
# parameter. It will be stopped automatically after the time set by the
# "idle_timeout" parameter. Use this start type if you wish the connection to
# only be active when it is needed.
#
# A bridge using the "once" start type will be started automatically when the
# broker starts but will not be restarted if the connection fails.
#start_type automatic
# Set the number of messages that need to be queued for a bridge with lazy
# start type to be restarted. Defaults to 10 messages.
# Must be less than max_queued_messages.
#threshold 10
# If try_private is set to true, the bridge will attempt to indicate to the
# remote broker that it is a bridge not an ordinary client. If successful, this
# means that loop detection will be more effective and that retained messages
# will be propagated correctly. Not all brokers support this feature so it may
# be necessary to set try_private to false if your bridge does not connect
# properly.
#try_private true
# Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism
# for brokers to tell clients that they do not support retained messages, but
# this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a
# v3.1.1 or v3.1 broker that does not support retained messages, set the
# bridge_outgoing_retain option to false. This will remove the retain bit on
# all outgoing messages to that bridge, regardless of any other setting.
#bridge_outgoing_retain true
# If you wish to restrict the size of messages sent to a remote bridge, use the
# bridge_max_packet_size option. This sets the maximum number of bytes for
# the total message, including headers and payload.
# Note that MQTT v5 brokers may provide their own maximum-packet-size property.
# In this case, the smaller of the two limits will be used.
# Set to 0 for "unlimited".
#bridge_max_packet_size 0
# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
# Either bridge_cafile or bridge_capath must be defined to enable TLS support
# for this bridge.
# bridge_cafile defines the path to a file containing the
# Certificate Authority certificates that have signed the remote broker
# certificate.
# bridge_capath defines a directory that will be searched for files containing
# the CA certificates. For bridge_capath to work correctly, the certificate
# files must have ".crt" as the file ending and you must run "openssl rehash
# <path to capath>" each time you add/remove a certificate.
#bridge_cafile
#bridge_capath
# If the remote broker has more than one protocol available on its port, e.g.
# MQTT and WebSockets, then use bridge_alpn to configure which protocol is
# requested. Note that WebSockets support for bridges is not yet available.
#bridge_alpn
# When using certificate based encryption, bridge_insecure disables
# verification of the server hostname in the server certificate. This can be
# useful when testing initial server configurations, but makes it possible for
# a malicious third party to impersonate your server through DNS spoofing, for
# example. Use this option in testing only. If you need to resort to using this
# option in a production environment, your setup is at fault and there is no
# point using encryption.
#bridge_insecure false
# Path to the PEM encoded client certificate, if required by the remote broker.
#bridge_certfile
# Path to the PEM encoded client private key, if required by the remote broker.
#bridge_keyfile
# -----------------------------------------------------------------
# PSK based SSL/TLS support
# -----------------------------------------------------------------
# Pre-shared-key encryption provides an alternative to certificate based
# encryption. A bridge can be configured to use PSK with the bridge_identity
# and bridge_psk options. These are the client PSK identity, and pre-shared-key
# in hexadecimal format with no "0x". Only one of certificate and PSK based
# encryption can be used on one
# bridge at once.
#bridge_identity
#bridge_psk
# =================================================================
# External config files
# =================================================================
# External configuration files may be included by using the
# include_dir option. This defines a directory that will be searched
# for config files. All files that end in '.conf' will be loaded as
# a configuration file. It is best to have this as the last option
# in the main file. This option will only be processed from the main
# configuration file. The directory specified must not contain the
# main configuration file.
# Files within include_dir will be loaded sorted in case-sensitive
# alphabetical order, with capital letters ordered first. If this option is
# given multiple times, all of the files from the first instance will be
# processed before the next instance. See the man page for examples.
#include_dir

View File

@@ -0,0 +1,19 @@
FROM smeagolworms4/mqtt-explorer:browser-1.0.3
COPY ./settings.json /mqtt-explorer/config/settings.json
ARG TARGETARCH
COPY entrypoint_wrapper.sh /entrypoint_wrapper.sh
RUN if [ ${TARGETARCH} != "amd64" ]; then \
mv /entrypoint.sh /wrapped_entrypoint.sh; \
cp /entrypoint_wrapper.sh /entrypoint.sh; \
fi; \
rm /entrypoint_wrapper.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD node node-server/server/dist/node-server/server/src/index.js \
--http-port=$HTTP_PORT \
--config-path=$CONFIG_PATH \
--http-user=$HTTP_USER \
--http-password=$HTTP_PASSWORD\
--ssl-key-path=$SSL_KEY_PATH\
--ssl-cert-path=$SSL_CERT_PATH

View File

@@ -0,0 +1,37 @@
#!/bin/sh
# ---------------------------------------------
# Architecture Warning Wrapper Script
#
# This script is used as an entrypoint wrapper to emit a warning
# when the container is not running on the officially supported
# amd64 (x86_64) architecture.
#
# It checks for the presence of a wrapped entrypoint script
# (/wrapped_entrypoint.sh) and executes it if found; otherwise,
# it falls back to executing the provided command directly.
#
# The warning is shown both before and after the wrapped command
# to ensure visibility.
# ---------------------------------------------
function print_warning {
echo -e "\033[0;31m"
echo "-------------------------------------------------------------"
echo "⚠️ WARNING: Unsupported Architecture Detected"
echo
echo "This Docker image is not running on the amd64 (x86_64) architecture."
echo "It is recommended to use the amd64-based image for full compatibility."
echo "Other architectures are not officially supported and may cause issues."
echo
echo "-------------------------------------------------------------"
echo -e "\033[0m"
}
print_warning
if [ -f /wrapped_entrypoint.sh ]; then
exec /wrapped_entrypoint.sh "$@"
else
exec "$@"
fi

View File

@@ -0,0 +1,26 @@
{
"ConnectionManager_connections": {
"mqtt-server": {
"configVersion": 1,
"certValidation": true,
"clientId": "mqtt-explorer-e1085971",
"id": "mqtt-server",
"name": "MQTT Server",
"encryption": false,
"subscriptions": [
{
"topic": "#",
"qos": 0
},
{
"topic": "$SYS/#",
"qos": 0
}
],
"type": "mqtt",
"host": "mqtt-server",
"port": 1883,
"protocol": "mqtt"
}
}
}

View File

@@ -0,0 +1,19 @@
FROM nodered/node-red:4.1.2
RUN npm install node-red-dashboard@3.6.6
RUN npm install node-red-contrib-ui-actions@0.1.8
RUN npm install node-red-node-ui-table@0.4.5
RUN npm install node-red-contrib-ui-level@0.1.46
COPY nodered-settings.js /data/settings.js
USER root
COPY entrypoint.sh /entrypoint.sh
ARG TARGETARCH
COPY entrypoint_wrapper.sh /entrypoint_wrapper.sh
RUN if [ ${TARGETARCH} != "amd64" ]; then \
mv /entrypoint.sh /wrapped_entrypoint.sh; \
cp /entrypoint_wrapper.sh /entrypoint.sh; \
fi; \
rm /entrypoint_wrapper.sh
USER node-red
ENTRYPOINT [ "/entrypoint.sh" ]

View File

@@ -0,0 +1,10 @@
#!/bin/sh
exec npm \
--no-update-notifier \
--no-fund \
start \
--cache /data/.npm \
-- \
--userDir /data \
"$@"

View File

@@ -0,0 +1,37 @@
#!/bin/sh
# ---------------------------------------------
# Architecture Warning Wrapper Script
#
# This script is used as an entrypoint wrapper to emit a warning
# when the container is not running on the officially supported
# amd64 (x86_64) architecture.
#
# It checks for the presence of a wrapped entrypoint script
# (/wrapped_entrypoint.sh) and executes it if found; otherwise,
# it falls back to executing the provided command directly.
#
# The warning is shown both before and after the wrapped command
# to ensure visibility.
# ---------------------------------------------
function print_warning {
echo -e "\033[0;31m"
echo "-------------------------------------------------------------"
echo "⚠️ WARNING: Unsupported Architecture Detected"
echo
echo "This Docker image is not running on the amd64 (x86_64) architecture."
echo "It is recommended to use the amd64-based image for full compatibility."
echo "Other architectures are not officially supported and may cause issues."
echo
echo "-------------------------------------------------------------"
echo -e "\033[0m"
}
print_warning
if [ -f /wrapped_entrypoint.sh ]; then
exec /wrapped_entrypoint.sh "$@"
else
exec "$@"
fi

View File

@@ -0,0 +1,20 @@
module.exports = {
// Flow file location
flowFile: 'flows.json',
// Enable projects
enableProjects: process.env.NODE_RED_ENABLE_PROJECTS === 'true',
// HTTP settings
httpNodeRoot: '/',
httpAdminRoot: '/',
// Logging
logging: {
console: {
level: "info",
metrics: false,
audit: false
}
}
};

View File

@@ -0,0 +1,30 @@
FROM maven:3.6.1-jdk-11
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
WORKDIR /steve
ENV DOCKERIZE_VERSION v0.6.1
RUN wget --no-verbose https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
RUN wget -qO- https://github.com/steve-community/steve/archive/steve-3.6.0.tar.gz | tar xz --strip-components=1
COPY main.properties src/main/resources/config/docker
COPY init.sh .
COPY keystore.jks .
ARG TARGETARCH
COPY entrypoint_wrapper.sh /entrypoint_wrapper.sh
RUN if [ ${TARGETARCH} != "amd64" ]; then \
ln -s /usr/local/bin/mvn-entrypoint.sh /wrapped_entrypoint.sh; \
cp /entrypoint_wrapper.sh /entrypoint.sh; \
else \
ln -s /usr/local/bin/mvn-entrypoint.sh /entrypoint.sh; \
fi; \
rm /entrypoint_wrapper.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD /steve/init.sh

View File

@@ -0,0 +1,37 @@
#!/bin/bash
# ---------------------------------------------
# Architecture Warning Wrapper Script
#
# This script is used as an entrypoint wrapper to emit a warning
# when the container is not running on the officially supported
# amd64 (x86_64) architecture.
#
# It checks for the presence of a wrapped entrypoint script
# (/wrapped_entrypoint.sh) and executes it if found; otherwise,
# it falls back to executing the provided command directly.
#
# The warning is shown both before and after the wrapped command
# to ensure visibility.
# ---------------------------------------------
function print_warning {
echo -e "\033[0;31m"
echo "-------------------------------------------------------------"
echo "⚠️ WARNING: Unsupported Architecture Detected"
echo
echo "This Docker image is not running on the amd64 (x86_64) architecture."
echo "It is recommended to use the amd64-based image for full compatibility."
echo "Other architectures are not officially supported and may cause issues."
echo
echo "-------------------------------------------------------------"
echo -e "\033[0m"
}
print_warning
if [ -f /wrapped_entrypoint.sh ]; then
exec /wrapped_entrypoint.sh "$@"
else
exec "$@"
fi

View File

@@ -0,0 +1,10 @@
#!/bin/bash
set -e # exit on any error
dockerize -wait tcp://ocpp-db:3306 -timeout 60s
if [ ! -f ".buildsuccess" ]; then
mvn clean package -Pdocker -Djdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2"
touch .buildsuccess
fi
java -jar target/steve.jar

View File

@@ -0,0 +1,57 @@
# Just to be backwards compatible with previous versions, this is set to "steve",
# since there might be already configured chargepoints expecting the older path.
# Otherwise, might as well be changed to something else or be left empty.
#
context.path = steve
# Database configuration
#
db.ip = ocpp-db
db.port = 3306
db.schema = ocpp-db
db.user = ocpp
db.password = ocpp
# Credentials for Web interface access
#
auth.user = admin
auth.password = 1234
# Jetty configuration
#
server.host = 0.0.0.0
server.gzip.enabled = false
# Jetty HTTP configuration
#
http.enabled = true
http.port = 8180
# Jetty HTTPS configuration
#
https.enabled = true
https.port = 8443
keystore.path = /steve/keystore.jks
keystore.password = 123456
# When the WebSocket/Json charge point opens more than one WebSocket connection,
# we need a mechanism/strategy to select one of them for outgoing requests.
# For allowed values see de.rwth.idsg.steve.ocpp.ws.custom.WsSessionSelectStrategyEnum.
#
ws.session.select.strategy = ALWAYS_LAST
# if BootNotification messages arrive (SOAP) or WebSocket connection attempts are made (JSON) from unknown charging
# stations, we reject these charging stations, because stations with these chargeBoxIds were NOT inserted into database
# beforehand. by setting this property to true, this behaviour can be modified to automatically insert unknown
# stations into database and accept their requests.
#
# CAUTION: setting this property to true is very dangerous, because we will accept EVERY BootNotification or WebSocket
# connection attempt from ANY sender as long as the sender knows the URL and sends a valid message.
#
auto.register.unknown.stations = false
### DO NOT MODIFY ###
steve.version = ${project.version}
git.describe = ${git.commit.id.describe}
db.sql.logging = false
profile = prod

View File

@@ -0,0 +1,3 @@
build
__pycache__
*.egg-info

View File

@@ -0,0 +1,24 @@
ev_setup_cmake_variables_python_wheel()
ev_add_pip_package(
NAME edm
SOURCE_DIRECTORY .
)
ev_is_python_venv_active(
RESULT_VAR IS_PYTHON_VENV_ACTIVE
)
if(NOT ${IS_PYTHON_VENV_ACTIVE})
message(WARNING "Python venv is not active. Please ensure that edm is available in your environment.")
else()
get_target_property(SOURCE_DIRECTORY ev_pip_package_edm SOURCE_DIRECTORY)
message(STATUS "Installing edm from: ${SOURCE_DIRECTORY}")
ev_pip_install_local(
PACKAGE_NAME "edm"
PACKAGE_SOURCE_DIRECTORY "${SOURCE_DIRECTORY}"
)
unset(EVEREST_DEPENDENCY_MANAGER CACHE)
find_program(EVEREST_DEPENDENCY_MANAGER edm HINTS ${EV_ACTIVATE_PYTHON_VENV_PATH_TO_VENV}/bin REQUIRED)
message(STATUS "Using edm from: ${EDM}")
endif()

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,232 @@
# Dependency Manager for EVerest
- [Dependency Manager for EVerest](#dependency-manager-for-everest)
- [Install and Quick Start](#install-and-quick-start)
- [Installing edm](#installing-edm)
- [Enabling CPM_SOURCE_CACHE](#enabling-cpm_source_cache)
- [Python packages needed to run edm](#python-packages-needed-to-run-edm)
- [Setting up CMake integration](#setting-up-cmake-integration)
- [Setting up a workspace](#setting-up-a-workspace)
- [Updating a workspace](#updating-a-workspace)
- [Using the EDM CMake module and dependencies.yaml](#using-the-edm-cmake-module-and-dependenciesyaml)
- [Modifying dependencies](#modifying-dependencies)
- [Create a workspace config from an existing directory tree](#create-a-workspace-config-from-an-existing-directory-tree)
- [Git information at a glance](#git-information-at-a-glance)
## Install and Quick Start
To install the **edm** dependency manager for EVerest you have to perform the following steps.
Please make sure you are running a sufficiently recent version of **Python3 (>=3.6)** and that you are able to install Python packages from source. Usually you just have to ensure that you have **pip**, **setuptools** and **wheel** available. Refer to [the Python *Installing Packages* documentation](https://packaging.python.org/tutorials/installing-packages/#requirements-for-installing-packages) for indepth guidance if any problems arise.
```bash
python3 -m pip install --upgrade pip setuptools wheel
```
### Installing edm
Now you can clone *this repository* and install **edm**:
(make sure you have set up your [ssh key](https://www.atlassian.com/git/tutorials/git-ssh) in github first!)
```bash
git clone git@github.com:EVerest/everest-dev-environment.git
cd everest-dev-environment/dependency_manager
python3 -m pip install .
edm --config ../everest-complete.yaml --workspace ~/checkout/everest-workspace
```
The last command registers the [**EDM** CMake module](#setting-up-cmake-integration) and creates a workspace in the *~/checkout/everest-workspace* directory from [a config that is shipped with this repository](../everest-complete.yaml).
The workspace will have the following structure containing all current dependencies for EVerest:
```bash
everest-workspace/
├── EVerest
├── everest-deploy-devkit
├── everest-dev-environment
├── everest-framework
├── everest-utils
├── liblog
├── libmodbus
├── libocpp
├── libsunspec
├── libtimer
├── open-plc-utils
├── RISE-V2G
└── workspace-config.yaml
```
The *workspace-config.yaml* contains a copy of the config that was used to create this workspace.
### Enabling CPM_SOURCE_CACHE
The **edm** dependency manager uses [CPM](https://github.com/cpm-cmake/CPM.cmake) for its CMake integration.
This means you *can* and **should** set the *CPM_SOURCE_CACHE* environment variable. This makes sure that dependencies that you do not manage in the workspace are not re-downloaded multiple times. For detailed information and other useful environment variables please refer to the [CPM Documentation](https://github.com/cpm-cmake/CPM.cmake/blob/master/README.md#CPM_SOURCE_CACHE).
```bash
export CPM_SOURCE_CACHE=$HOME/.cache/CPM
```
### Python packages needed to run edm
The following Python3 packages are needed to run the **edm** dependency manager.
If you installed **edm** using the guide above they were already installed automatically.
- Python >= 3.6
- Jinja2 >= 3.0
- PyYAML >= 5.4
## Setting up and updating a workspace
For letting **edm** do the work of setting up an initial EVerest workspace,
do this:
```bash
edm init --workspace ~/checkout/everest-workspace
```
If you are currently in the *everest-workspace* directory the following command has the same effect:
```bash
edm init
```
For using a dedicated release version, you can do this:
```bash
edm init 2023.7.0
```
In this example, version 2023.7.0 is pulled from the server. This will only work if
you previous code is not in a "dirty" state.
## Using the EDM CMake module and dependencies.yaml
To use **edm** from CMake you have to add the following line to the top-level *CMakeLists.txt* file in the respective source repository:
```cmake
find_package(EDM REQUIRED)
```
The **EDM** CMake module will be discovered automatically if you [registered the CMake module in the way it described in the *Setting up CMake integration* section of this readme](#setting-up-cmake-integration).
To define dependencies you can now add a **dependencies.yaml** file to your source repository. It should look like this:
```yaml
---
liblog:
git: git@github.com:EVerest/liblog.git
git_tag: main
options: ["BUILD_EXAMPLES OFF"]
libtimer:
git: git@github.com:EVerest/libtimer.git
git_tag: main
options: ["BUILD_EXAMPLES OFF"]
```
If you want to conditionally include some dependencies, eg. for testing, you can do this in the following way:
```yaml
catch2:
git: https://github.com/catchorg/Catch2.git
git_tag: v3.4.0
cmake_condition: "BUILD_TESTING"
```
Here *cmake_condition* can be any string that CMake can use in an if() block. Please be aware that any variables you use here must be defined before a call to *evc_setup_edm()* is made in your CMakeLists.txt
## Selective library consumption
If your project only needs specific everest-core libraries (e.g. `liblog`, `everest-util`, `everest-io`, `libocpp`) without building the full module framework, use the `EVEREST_LIBS_ONLY` and `EVEREST_INCLUDE_LIBS` CMake options.
In your project's `dependencies.yaml`:
```yaml
everest-core:
git: https://github.com/EVerest/everest-core.git
git_tag: 2026.02.0
options:
- "EVEREST_LIBS_ONLY ON"
- "EVEREST_INCLUDE_LIBS log;util;io"
```
Or directly via CMake:
```bash
cmake -S . -B build \
-DEVEREST_LIBS_ONLY=ON \
-DEVEREST_INCLUDE_LIBS="log;util;io"
```
| Option | Default | Description |
|---|---|---|
| `EVEREST_LIBS_ONLY` | OFF | Skip modules, applications, config — only build libraries |
| `EVEREST_INCLUDE_LIBS` | (empty) | Semicolon-separated allowlist; transitive deps auto-resolved |
| `EVEREST_EXCLUDE_LIBS` | (empty) | Semicolon-separated blocklist of libraries to skip |
Transitive dependencies are resolved automatically. For example, `EVEREST_INCLUDE_LIBS="ocpp"` will also build `log`, `timer`, `evse_security`, `sqlite`, and `cbv2g`.
See the [EDM documentation](../../docs/source/explanation/dev-tools/edm.rst) for the full list of available libraries and their dependency chains.
## Modifying dependencies
To change dependency git URLs you can set the *EVEREST_MODIFY_DEPENDENCIES_URLS* environment variable to a string containing prefixes and replacements delimited by whitespace characters.
For example:
```bash
EVEREST_MODIFY_DEPENDENCIES_URLS="prefix=https://github.com/EVerest/ replace=git@github.com:EVerest/"
```
This would change all dependency git URLs that start with *https://github.com/EVerest/* to *git@github.com:EVerest/*.
Multiple prefix and replace pairs can be chained together delimited by whitespace characters.
For example:
```bash
EVEREST_MODIFY_DEPENDENCIES_URLS="prefix=https://github.com/EVerest/ replace=git@github.com:EVerest/ prefix=https://github.com/EVerest/everest-framework.git replace=https://github.com/EVerest/everest-framework.git"
```
This would change all dependency git URLs that start with *https://github.com/EVerest/* to *git@github.com:EVerest/* as well as keeping the dependency https URL of *https://github.com/EVerest/everest-framework.git* as *https://github.com/EVerest/everest-framework.git*.
Additionally you can set the *EVEREST_MODIFY_DEPENDENCIES* environment variable to a file containing modifications to the projects dependencies.yaml files when running cmake:
```bash
EVEREST_MODIFY_DEPENDENCIES=../dependencies_modified.yaml cmake -S . -B build
```
The *dependencies_modified.yaml* file can contain something along these lines:
```yaml
nlohmann_json:
git: null # this makes edm look for nlohmann_json via find_package
libfmt:
rename: fmt # if find_package needs a different dependency name you can rename it
git: null
catch2:
git_tag: v1.2.3 # if you want to select a different git tag for a build this is also possible
```
## Create a workspace config from an existing directory tree
Suppose you already have a directory tree that you want to save into a config file.
You can do this with the following command:
```bash
edm --create-config custom-config.yaml
```
This is a short form of
```bash
edm --create-config custom-config.yaml --include-remotes git@github.com:EVerest/*
```
and only includes repositories from the *EVerest* namespace. You can add as many remotes to this list as you want.
For example if you only want to include certain repositories you can use the following command.
```bash
edm --create-config custom-config.yaml --include-remotes git@github.com:EVerest/everest* git@github.com:EVerest/liblog.git
```
If you want to include all repositories, including external dependencies, in the config you can use the following command.
```bash
edm --create-config custom-config.yaml --external-in-config
```
## Git information at a glance
You can get a list of all git repositories in the current directory and their state using the following command.
```bash
edm --git-info --git-fetch
```
If you want to know the state of all repositories in a workspace you can use the following command.
```bash
edm --workspace ~/checkout/everest-workspace --git-info --git-fetch
```
This creates output that is similar to the following example.
```bash
[edm]: Git info for "~/checkout/everest-workspace":
[edm]: Using git-fetch to update remote information. This might take a few seconds.
[edm]: "everest-dev-environment" @ branch: main [remote: origin/main] [behind 6] [clean]
[edm]: "everest-framework" @ branch: main [remote: origin/main] [dirty]
[edm]: "everest-deploy-devkit" @ branch: main [remote: origin/main] [clean]
[edm]: "libtimer" @ branch: main [remote: origin/main] [dirty]
[edm]: 2/4 repositories are dirty.
```

View File

@@ -0,0 +1,12 @@
#!/bin/bash
##
## SPDX-License-Identifier: Apache-2.0
## Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
##
echo "generating bash-completion file"
SRC_DIR="$(dirname "${BASH_SOURCE[0]}")/src"
echo "Using module found in ${SRC_DIR}"
cd "${SRC_DIR}"
BASH_COMPLETION_FILE_DIR="$(pwd)"
BASH_COMPLETION_FILE="${BASH_COMPLETION_FILE_DIR}/edm_tool/edm-completion.bash"
shtab --shell=bash -u edm_tool.get_parser --prog edm > "${BASH_COMPLETION_FILE}"

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

View File

@@ -0,0 +1,3 @@
Jinja2>=2.11
PyYAML>=5.3
requests>=2

View File

@@ -0,0 +1,38 @@
[metadata]
name = edm_tool
version = attr: edm_tool.__version__
description= A simple dependency manager
long_description = file: README.md
long_description_content_type= text/markdown
url= https://github.com/EVerest/everest-dev-environment
author = Kai-Uwe Hermann
author_email = kai-uwe.hermann@pionix.de
classifiers =
Development Status :: 3 - Alpha
Intended Audience :: Developers
Topic :: Software Development :: Build Tools
License :: OSI Approved :: Apache Software License
[options]
packages = edm_tool
package_dir =
= src
python_requires = >=3.6
install_requires =
Jinja2>=2.11
PyYAML>=5.3
requests>=2
[options.entry_points]
console_scripts =
edm = edm_tool:main
[options.package_data]
edm_tool =
templates/cpm.jinja
cmake/CPM.cmake
cmake/EDMConfig.cmake
edm-completion.bash
[pycodestyle]
max-line-length = 120

View File

@@ -0,0 +1,9 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
"""Everest Dependency Manager."""
from setuptools import setup, find_packages
setup(
# see setup.cfg
)

View File

@@ -0,0 +1,18 @@
#
# SPDX-License-Identifier: Apache-2.0
# Copyright Pionix GmbH and Contributors to EVerest
#
"""Everest Dependency Manager."""
from edm_tool import edm
__version__ = "0.8.0"
def get_parser():
"""Return the command line parser."""
return edm.get_parser(__version__)
def main():
"""Main entrypoint of edm."""
edm.main(get_parser())

View File

@@ -0,0 +1,88 @@
"Bazel related functions for edm_tool."
import yaml
from typing import List, Optional, Dict
def _format_optional_string(value: Optional[str]):
"""Formats a string value as a string literal (with quotes) or `None` if the value is None."""
if value is None:
return "None"
return f'"{value}"'
def _is_commit(revision: str):
# Revision is a commit if it is a hexadecimal 40-character string
return len(revision) == 40 and all(c in "0123456789abcdef" for c in revision.lower())
def _get_depname_for_label(label: str) -> str:
build, depname, bazel = label.split(":")[1].split(".")
if build != "BUILD" or bazel != "bazel":
raise ValueError(f"Invalid build file name: {label}")
return depname
def _parse_build_file_labels(labels: Optional[List[str]]) -> Dict[str, str]:
# For easier matching of build files with dependencies
# we convert the list of build files:
# ```
# [
# "@workspace//path/to/build:BUILD.<depname>.bazel",
# ...
# ]
# ```
# into a dictionary:
# ```
# {
# "<depname>": "@workspace//path/to/build:BUILD.<depname>.bazel",
# ...
# }
# ```
# and check that all build files have proper names.
if labels is None:
return {}
return dict((_get_depname_for_label(label), label) for label in labels)
def generate_deps(args):
"Parse the dependencies.yaml and print content of *.bzl file to stdout."
with open(args.dependencies_yaml, 'r', encoding='utf-8') as f:
deps = yaml.safe_load(f)
build_files = _parse_build_file_labels(args.build_file)
for name in build_files:
if name not in deps:
raise ValueError(f"Build file {name} does not have a corresponding dependency in {args.dependencies_yaml}")
print("""
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
def edm_deps():""")
for name, desc in deps.items():
repo = desc["git"]
# The parameter is called `git_tag` but it can be a tag or a commit
revision = desc["git_tag"]
tag = None
commit = None
if _is_commit(revision):
commit = revision
else:
tag = revision
build_file = build_files.get(name)
print(
f"""
maybe(
git_repository,
name = "{name}",
remote = "{repo}",
tag = {_format_optional_string(tag)},
commit = {_format_optional_string(commit)},
build_file = {_format_optional_string(build_file)},
)
"""
)

View File

@@ -0,0 +1,144 @@
# AUTOMATCALLY GENERATED by `shtab`
_shtab_edm_tool_option_strings=('-h' '--help' '--version' '--workspace' '--working_dir' '--out' '--include_deps' '--config' '--create-vscode-workspace' '--update' '--cmake' '--verbose' '--nocolor' '--install-bash-completion' '--create-config' '--external-in-config' '--include-remotes' '--create-snapshot' '--git-info' '--git-fetch' '--git-pull')
_shtab_edm_tool_pos_0_nargs=A...
_shtab_edm_tool__h_nargs=0
_shtab_edm_tool___help_nargs=0
_shtab_edm_tool___version_nargs=0
_shtab_edm_tool___include_deps_nargs=0
_shtab_edm_tool___create_vscode_workspace_nargs=0
_shtab_edm_tool___update_nargs=0
_shtab_edm_tool___cmake_nargs=0
_shtab_edm_tool___verbose_nargs=0
_shtab_edm_tool___nocolor_nargs=0
_shtab_edm_tool___install_bash_completion_nargs=0
_shtab_edm_tool___external_in_config_nargs=0
_shtab_edm_tool___include_remotes_nargs=*
_shtab_edm_tool___create_snapshot_nargs=?
_shtab_edm_tool___git_info_nargs=0
_shtab_edm_tool___git_fetch_nargs=0
_shtab_edm_tool___git_pull_nargs=*
# $1=COMP_WORDS[1]
_shtab_compgen_files() {
compgen -f -- $1 # files
}
# $1=COMP_WORDS[1]
_shtab_compgen_dirs() {
compgen -d -- $1 # recurse into subdirs
}
# $1=COMP_WORDS[1]
_shtab_replace_nonword() {
echo "${1//[^[:word:]]/_}"
}
# set default values (called for the initial parser & any subparsers)
_set_parser_defaults() {
local subparsers_var="${prefix}_subparsers[@]"
sub_parsers=${!subparsers_var}
local current_option_strings_var="${prefix}_option_strings[@]"
current_option_strings=${!current_option_strings_var}
completed_positional_actions=0
_set_new_action "pos_${completed_positional_actions}" true
}
# $1=action identifier
# $2=positional action (bool)
# set all identifiers for an action's parameters
_set_new_action() {
current_action="${prefix}_$(_shtab_replace_nonword $1)"
local current_action_compgen_var=${current_action}_COMPGEN
current_action_compgen="${!current_action_compgen_var}"
local current_action_choices_var="${current_action}_choices"
current_action_choices="${!current_action_choices_var}"
local current_action_nargs_var="${current_action}_nargs"
if [ -n "${!current_action_nargs_var}" ]; then
current_action_nargs="${!current_action_nargs_var}"
else
current_action_nargs=1
fi
current_action_args_start_index=$(( $word_index + 1 ))
current_action_is_positional=$2
}
# Notes:
# `COMPREPLY`: what will be rendered after completion is triggered
# `completing_word`: currently typed word to generate completions for
# `${!var}`: evaluates the content of `var` and expand its content as a variable
# hello="world"
# x="hello"
# ${!x} -> ${hello} -> "world"
_shtab_edm_tool() {
local completing_word="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=()
prefix=_shtab_edm_tool
word_index=0
_set_parser_defaults
word_index=1
# determine what arguments are appropriate for the current state
# of the arg parser
while [ $word_index -ne $COMP_CWORD ]; do
local this_word="${COMP_WORDS[$word_index]}"
if [[ -n $sub_parsers && " ${sub_parsers[@]} " =~ " ${this_word} " ]]; then
# valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
_set_parser_defaults
fi
if [[ " ${current_option_strings[@]} " =~ " ${this_word} " ]]; then
# a new action should be acquired (due to recognised option string or
# no more input expected from current action);
# the next positional action can fill in here
_set_new_action $this_word false
fi
if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index >= \
$current_action_nargs )); then
$current_action_is_positional && let "completed_positional_actions += 1"
_set_new_action "pos_${completed_positional_actions}" true
fi
let "word_index+=1"
done
# Generate the completions
if [[ "${completing_word}" == -* ]]; then
# optional argument started: use option strings
COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
else
# use choices & compgen
local IFS=$'\n'
COMPREPLY=( $(compgen -W "${current_action_choices}" -- "${completing_word}") \
$([ -n "${current_action_compgen}" ] \
&& "${current_action_compgen}" "${completing_word}") )
fi
return 0
}
complete -o filenames -F _shtab_edm_tool edm

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
set(ENV{EVEREST_EDM_WORKSPACE} {{ workspace["workspace"] }})
set(CPM_USE_NAMED_CACHE_DIRECTORIES ON)
{% for dep in checkout %}
set(CPM_{{ dep["name"] }}_SOURCE "{{ dep["path"] }}")
{% endfor %}
{% for name, value in dependencies.items() %}
if("{{name}}" IN_LIST EVEREST_EXCLUDE_DEPENDENCIES)
message(STATUS "Excluding dependency {{name}}")
{% if "cmake_condition" in value and value["cmake_condition"]|length > 0 %}
elseif({{ value["cmake_condition"] }})
{% else %}
else()
{% endif %}
{% if value and "git" in value %}
CPMAddPackage(
NAME {{ name }}
GIT_REPOSITORY {{ value["git"] }}
{% if "git_tag" in value %}
GIT_TAG {{ value["git_tag"] }}
{% endif %}
{% if "options" in value and value["options"]|length > 0 %}
OPTIONS
{{value["options"]|quote|join(" ")}}
{% endif %}
{% if "prevent_install" in value and value["prevent_install"] %}
EXCLUDE_FROM_ALL YES
{% endif %}
)
{% else %}
find_package(
{{ name }}
{% if value and "components" in value and value["components"]|length > 0 %}
COMPONENTS
{{value["components"]|quote|join(" ")}}
{% endif %}
{% if not value or "optional" not in value or not value["optional"] %}
REQUIRED
{% endif %}
)
{% endif %}
{% if value and "alias" in value %}
if({{name}}_ADDED)
add_library({{value["alias"]["name"]}} ALIAS {{value["alias"]["target"]}})
endif()
{% endif %}
{% if "cmake_condition" in value and value["cmake_condition"]|length > 0 %}
else()
message(STATUS "Excluding dependency {{name}} based on cmake_condition")
{% endif %}
endif()
{% endfor %}
execute_process(
COMMAND "${EVEREST_DEPENDENCY_MANAGER}" release --everest-dir ${PROJECT_SOURCE_DIR} --build-dir ${CMAKE_BINARY_DIR} --out ${CMAKE_BINARY_DIR}/release.json
)
install(
FILES "${CMAKE_BINARY_DIR}/release.json"
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/everest"
)

View File

@@ -0,0 +1,690 @@
#!/usr/bin/env bash
set -e
# Default values
EVEREST_TOOL_BRANCH="main"
# Script directory - where devrd is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# .devcontainer directory is always relative to the script location
DEVCONTAINER_DIR="${SCRIPT_DIR}/../../.devcontainer"
# .env file is always in the .devcontainer directory (relative to script)
ENV_FILE="${DEVCONTAINER_DIR}/.env"
# Function to load HOST_WORKSPACE_FOLDER from .env file
# Usage: load_workspace_from_env [fallback]
# If fallback is provided and workspace not found in .env, returns fallback
# If no fallback provided, returns empty string (for use with ${var:-default} syntax)
load_workspace_from_env() {
local fallback="$1"
if [ -f "$ENV_FILE" ]; then
local workspace=$(grep "^HOST_WORKSPACE_FOLDER=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -n "$workspace" ]; then
echo "$workspace"
return
fi
fi
# If fallback provided and workspace not found, return fallback
if [ -n "$fallback" ]; then
echo "$fallback"
fi
}
# HOST_WORKSPACE_FOLDER is the folder that is mapped to /workspace in the container
# Priority: 1) Command line/env var, 2) .env file, 3) Current directory
HOST_WORKSPACE_FOLDER="${HOST_WORKSPACE_FOLDER:-$(load_workspace_from_env)}"
HOST_WORKSPACE_FOLDER="${HOST_WORKSPACE_FOLDER:-$(pwd)}"
# Docker Compose project name (defaults to workspace folder name with _devcontainer suffix, can be overridden)
# This matches VSC's naming convention: {workspace-folder-name}_devcontainer-{service-name}-1
# If needed (and not running in VSCode), can be changed by setting the DOCKER_COMPOSE_PROJECT_NAME environment variable.
DOCKER_COMPOSE_PROJECT_NAME="${DOCKER_COMPOSE_PROJECT_NAME:-$(basename "$HOST_WORKSPACE_FOLDER" | tr \"A-Z\" \"a-z\")_devcontainer}"
# Function to detect if running inside container
is_inside_container() {
# Check for /.dockerenv (standard Docker indicator)
[ -f /.dockerenv ] && return 0
# Check if /workspace exists and is mounted (devcontainer specific)
[ -d /workspace ] && [ -f /workspace/.devcontainer/devrd ] && return 0
return 1
}
# Function to show error when command is run from inside container
show_inside_container_error() {
local cmd_name="${1:-this command}"
echo "✖ Error: This command cannot be run from inside the container"
echo ""
echo "You are currently inside the development container."
echo "Please run this command from the host system instead:"
echo ""
echo " 1. Exit the container (type 'exit' or press Ctrl+D)"
echo " 2. Run the command from your host terminal:"
echo " ./devrd $cmd_name"
echo ""
exit 1
}
# Function to run docker compose with static project name
# Compose files are always relative to the script's .devcontainer directory
docker_compose() {
docker compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \
-f "${DEVCONTAINER_DIR}/docker-compose.yml" \
-f "${DEVCONTAINER_DIR}/general-devcontainer/docker-compose.devcontainer.yml" "$@"
}
# Function to validate folder path
validate_folder() {
local folder="$1"
# Convert relative path to absolute
case "$folder" in
/*) ;; # Already absolute
*) folder="$(cd "$folder" && pwd)" ;; # Convert relative to absolute
esac
# Check if folder exists
if [ ! -d "$folder" ]; then
echo "Error: Folder '$folder' does not exist"
exit 1
fi
# Check if folder is readable
if [ ! -r "$folder" ]; then
echo "Error: Folder '$folder' is not accessible (permission denied)"
exit 1
fi
echo "$folder"
}
# Function to generate .env file
generate_env() {
if is_inside_container; then
show_inside_container_error "env"
fi
# Process command line options
if [ -n "$ENV_OPTIONS" ]; then
set -- $ENV_OPTIONS
while [ $# -gt 0 ]; do
case "$1" in
-v|--version)
EVEREST_TOOL_BRANCH="$2"
shift 2
;;
-w|--workspace)
HOST_WORKSPACE_FOLDER="$2"
shift 2
;;
*)
shift
;;
esac
done
fi
# Set workspace folder
if [ -n "$HOST_WORKSPACE_FOLDER" ]; then
HOST_WORKSPACE_FOLDER=$(validate_folder "$HOST_WORKSPACE_FOLDER")
else
HOST_WORKSPACE_FOLDER="$(pwd)"
fi
# Get commit hash
COMMIT_HASH=$(git ls-remote https://github.com/EVerest/everest-dev-environment.git ${EVEREST_TOOL_BRANCH} | cut -f1 2>/dev/null || echo "")
# Check if we need to update existing file
local needs_update=false
if [ -f "$ENV_FILE" ] && [ -s "$ENV_FILE" ]; then
# File exists, check if we have options that require updates
if [ -n "$ENV_OPTIONS" ]; then
needs_update=true
fi
fi
if [ ! -f "$ENV_FILE" ] || [ ! -s "$ENV_FILE" ] || [ "$needs_update" = true ]; then
cat > "$ENV_FILE" << EOF
# Auto-generated by devrd script
ORGANIZATION_ARG=EVerest
REPOSITORY_HOST=github.com
REPOSITORY_USER=git
COMMIT_HASH=$COMMIT_HASH
EVEREST_TOOL_BRANCH=$EVEREST_TOOL_BRANCH
UID=$(id -u)
GID=$(id -g)
HOST_WORKSPACE_FOLDER=$HOST_WORKSPACE_FOLDER
EOF
if [ "$needs_update" = true ]; then
echo "Updated .env file"
else
echo "Generated .env file"
fi
else
echo "Found existing .env file"
cat "$ENV_FILE"
fi
}
# Function to build the container
build_container() {
if is_inside_container; then
show_inside_container_error "build"
fi
echo "Building development container..."
docker_compose --profile all build
}
# Function to get actual port mapping from docker compose
get_port_mapping() {
local service_name=$1
local internal_port=$2
# Get the actual port mapping from docker compose
local port_mapping=$(docker_compose port $service_name $internal_port 2>/dev/null)
if [ -n "$port_mapping" ]; then
# Extract just the host port (remove the host part)
echo "$port_mapping" | sed 's/.*://'
else
echo ""
fi
}
# Function to display container links and tips
display_container_status() {
echo ""
echo "Container Services Summary:"
echo "=============================="
# Get actual port mappings from docker compose
local mqtt_explorer_port=$(get_port_mapping mqtt-explorer 4000)
local steve_http_port=$(get_port_mapping steve 8180)
# Display links with actual ports
if [ -n "$mqtt_explorer_port" ]; then
echo "MQTT Explorer: http://localhost:$mqtt_explorer_port"
else
echo "MQTT Explorer: currently not running"
fi
if [ -n "$steve_http_port" ]; then
echo "Steve (HTTP): http://localhost:$steve_http_port"
else
echo "Steve (HTTP): currently not running"
fi
# Check if Node-RED is running
if docker_compose ps | grep -q "nodered"; then
echo "Node-RED UI: http://localhost:1880/ui"
else
echo "Node-RED UI: currently not running"
fi
echo ""
echo "Tips:"
echo " • MQTT Explorer: Browse and debug MQTT topics"
echo " • Steve: OCPP backend management interface"
echo " • Node-RED: Web-based UI for SIL simulations"
echo " • Use './devrd prompt' to access the container shell"
echo " • Use './devrd nodered-flows' to see available flows"
echo ""
}
# Function to start containers using profiles
start_compose_profile() {
if is_inside_container; then
show_inside_container_error "start"
fi
local profile_or_service="$1"
if [ -n "$profile_or_service" ]; then
echo "Starting containers for profile/service: $profile_or_service..."
docker_compose --profile "$profile_or_service" up -d
else
echo "Starting the development container and all services..."
docker_compose --profile all up -d
fi
# Display workspace mapping
echo "Workspace mapping: $HOST_WORKSPACE_FOLDER → /workspace"
echo ""
# Display container links
display_container_status
}
# Function to stop containers using profiles or container name pattern
stop_compose_profile() {
if is_inside_container; then
show_inside_container_error "stop"
fi
local profile_or_pattern="$1"
if [ -n "$profile_or_pattern" ]; then
# Check if it's a valid profile name
case "$profile_or_pattern" in
mqtt|ocpp|sil|all)
echo "Stopping containers for profile: $profile_or_pattern..."
docker_compose --profile "$profile_or_pattern" stop
;;
*)
# Treat as container name pattern
echo "Stopping containers matching pattern: $profile_or_pattern..."
local containers=$(docker ps --format "{{.Names}}" | grep -E "($profile_or_pattern)" || true)
if [ -z "$containers" ]; then
echo "No running containers found matching pattern: $profile_or_pattern"
return 1
fi
echo "$containers" | while read container; do
echo "Stopping container: $container"
docker stop "$container" 2>/dev/null || echo "Failed to stop container: $container"
done
;;
esac
else
echo "Stopping the development container and all services..."
docker_compose --profile all stop
fi
}
# Function to purge everything
purge_everything() {
if is_inside_container; then
show_inside_container_error "purge"
fi
local purge_pattern="${1:-$(basename "$HOST_WORKSPACE_FOLDER")}"
local current_project="$(basename "$HOST_WORKSPACE_FOLDER")"
echo "Purging all devcontainer resources for pattern: $purge_pattern..."
# Only use docker_compose down if purging the current project
if [ "$purge_pattern" = "$current_project" ]; then
echo "Stopping and removing containers for current project..."
docker_compose down -v --remove-orphans
else
echo "Purging resources for different project pattern: $purge_pattern"
echo "Skipping docker-compose cleanup (not current project)"
fi
# Remove all images related to the project
echo "Removing devcontainer images..."
docker images --format "table {{.Repository}}:{{.Tag}}" | grep -E "($purge_pattern)" | awk '{print $1}' | xargs -r docker rmi -f
# Remove all volumes related to the project (with force if needed)
echo "Removing devcontainer volumes..."
docker volume ls --format "{{.Name}}" | grep -E "($purge_pattern)" | while read volume; do
echo "Removing volume: $volume"
docker volume rm -f "$volume" 2>/dev/null || echo "Volume $volume could not be removed (may be in use)"
done
# Ask user if they want to purge CPM cache volume
echo ""
echo "CPM source cache volume (everest-cpm-source-cache) is shared across all workspaces."
read -p "Do you want to purge the CPM cache volume as well? [y/N]: " purge_cache
purge_cache="${purge_cache:-N}"
if [[ "$purge_cache" =~ ^[Yy]$ ]]; then
echo "Removing CPM cache volume..."
if docker volume rm everest-cpm-source-cache 2>/dev/null; then
echo "✔ CPM cache volume removed"
else
echo "⚠ CPM cache volume could not be removed (may be in use or not exist)"
fi
else
echo "Keeping CPM cache volume (will be reused for faster builds)"
fi
# Remove any dangling images and containers
echo ""
echo "Cleaning up dangling resources..."
docker system prune -f
echo ""
echo "✔ Purge complete! All devcontainer resources have been removed."
}
# Function to check if SSH agent is running
check_ssh_agent() {
if [ -z "$SSH_AUTH_SOCK" ] || ! ssh-add -l >/dev/null 2>&1; then
echo "Error: SSH agent is not running or no keys are loaded."
echo "Please start the SSH agent and add your keys:"
echo " eval \$(ssh-agent)"
echo " ssh-add ~/.ssh/id_rsa # or your private key"
echo "Or if you're using a different key:"
echo " ssh-add ~/.ssh/your_private_key"
exit 1
fi
}
# Function to execute a command in the container
exec_devcontainer() {
if is_inside_container; then
echo "✖ You're already inside the container."
echo ""
echo "To run a command, just execute it directly:"
if [ $# -gt 0 ]; then
echo " $@"
else
echo " <your-command>"
fi
exit 1
fi
echo "Checking if development container is running..."
# Check if the devcontainer service is running
if ! docker_compose ps devcontainer | grep -q "Up"; then
echo "Error: Development container is not running."
echo "Please start the container first with: ./devrd start"
echo "Or build and start with: ./devrd build && ./devrd start"
exit 1
fi
echo "Executing command in development container..."
run_in_devcontainer "$@"
}
# Function to get a shell prompt in the container
prompt_devcontainer() {
if is_inside_container; then
echo "✖ You're already inside the container shell."
exit 1
fi
echo "Starting shell in development container..."
exec_devcontainer /bin/bash
}
# Helper function to check if Node-RED is running and get the URL
# Sets nodered_url variable and returns 0 if running, 1 if not
check_nodered_running() {
if is_inside_container; then
nodered_url="http://nodered:1880"
curl -s "$nodered_url/flows" >/dev/null 2>&1 && return 0
else
nodered_url="http://localhost:1880"
docker_compose ps | grep -q "nodered" && return 0
fi
return 1
}
# Helper function to execute a command in the container
# Usage: run_in_devcontainer [--no-tty] <command> [args...]
# Executes directly if inside container, via docker_compose exec if on host
# No error checking - assumes container is running when called from host
# Use --no-tty for non-interactive commands that need output capture
run_in_devcontainer() {
local no_tty=false
if [ "$1" = "--no-tty" ]; then
no_tty=true
shift
fi
if is_inside_container; then
"$@"
else
if [ "$no_tty" = true ]; then
docker_compose exec -T devcontainer "$@"
else
docker_compose exec devcontainer "$@"
fi
fi
}
# Function to list available flows
list_nodered_flows() {
echo ""
echo "Available Node-RED Flows:"
echo "============================="
# Check if Node-RED is running
if ! check_nodered_running; then
echo "✖ Node-RED container is not running"
echo "Please start with './devrd start' first"
return 1
fi
# Find all flow files in the workspace
local flows
if is_inside_container; then
flows=$(find /workspace -name "*-flow.json" -type f 2>/dev/null | sort)
else
flows=$(docker_compose exec -T devcontainer find /workspace -name "*-flow.json" -type f 2>/dev/null | sort)
fi
if [ -z "$flows" ]; then
echo "No flow files found in workspace"
echo ""
echo "Expected pattern: *-flow.json"
echo "Search location: /workspace"
return 1
fi
echo "Found $(echo "$flows" | wc -l) flow file(s):"
echo ""
for flow in $flows; do
# Remove /workspace/ prefix to get relative path from workspace root
local relative_path=$(echo "$flow" | sed 's|^/workspace/||')
echo " Path: $relative_path"
done
echo ""
echo "Usage: ./devrd flow <flow-file-path>"
echo "Example: ./devrd flow EVerest/config/nodered/config-sil-dc-flow.json"
echo ""
}
# Function to switch flow using REST API
switch_nodered_flow() {
local flow_path="$1"
if [ -z "$flow_path" ]; then
echo "Error: Please specify a flow file path"
echo ""
echo "Available flows:"
list_nodered_flows
return 1
fi
# Check if Node-RED is running
if ! check_nodered_running; then
echo "✖ Node-RED container is not running"
echo "Please start with './devrd start' first"
return 1
fi
# Construct full path in container
local full_path="/workspace/$flow_path"
# Check if file exists and is readable, then copy to temp file
if ! run_in_devcontainer --no-tty test -r "$full_path"; then
echo "✖ Flow file not found or not readable: $flow_path"
echo ""
echo "Available flows:"
list_nodered_flows
return 1
fi
# Copy flow to temporary file
run_in_devcontainer --no-tty cat "$full_path" > /tmp/flows.json
echo "Switching Node-RED to flow: $(basename "$flow_path")"
echo "Source: $flow_path"
# Process environment variables in the flow JSON
# Replace "broker": "localhost" with "broker": "mqtt-server"
sed -i 's/"broker": "localhost"/"broker": "mqtt-server"/g' /tmp/flows.json
# Deploy flow via Node-RED REST API
echo "Deploying flow via Node-RED API..."
local response=$(curl -s -w "%{http_code}" -X POST "$nodered_url/flows" \
-H "Content-Type: application/json" \
-d @/tmp/flows.json)
local http_code="${response: -3}"
if [ "$http_code" = "200" ] || [ "$http_code" = "204" ]; then
echo "✔ Node-RED flow deployed successfully via API!"
if is_inside_container; then
echo "Access at: http://nodered:1880/ui (from container) or http://localhost:1880/ui (from host)"
else
echo "Access at: http://localhost:1880/ui"
fi
else
echo "✖ Failed to deploy flow via API (HTTP $http_code)"
echo "Response: ${response%???}"
return 1
fi
# Clean up temporary file
rm -f /tmp/flows.json
}
# Function to display help
show_help() {
echo "Usage: $0 [COMMAND] [OPTIONS]"
echo ""
echo "Commands:"
echo " env Generate .env file with repository information (default)"
echo " build Build the development container"
echo " start [profile] Start containers (profiles: mqtt, ocpp, sil, all)"
echo " stop [profile|pattern] Stop containers by profile (mqtt, ocpp, sil, all) or container name pattern"
echo " purge [pattern] Remove all devcontainer resources (containers, images, volumes)"
echo " Optional pattern to match (default: current folder name)"
echo " exec <command> Execute a command in the development container (requires the container to be running)"
echo " prompt Get a shell prompt in the development container (requires the container to be running)"
echo " flows List available flows"
echo " flow <path> Switch to specific flow file"
echo ""
echo "Options (for env command only):"
echo " -v, --version VERSION Everest tool branch (default: $EVEREST_TOOL_BRANCH, preserves existing if not specified)"
echo " -w, --workspace DIR Workspace directory to map to /workspace in container (default: current directory)"
echo " --help Display this help message"
echo ""
echo "Examples:"
echo " $0 env # Generate .env file with repository information"
echo " $0 build # Build container"
echo " $0 start # Start all containers"
echo " $0 start sil # Start SIL simulation tools (Node-RED, MQTT Explorer)"
echo " $0 start ocpp # Start OCPP services (Steve, OCPP DB, MQTT)"
echo " $0 start mqtt # Start only MQTT server"
echo " $0 stop sil # Stop SIL simulation tools"
echo " $0 stop ev-ws # Stop all containers matching pattern 'ev-ws'"
echo " $0 purge # Remove all devcontainer resources for current folder"
echo " $0 purge my-project # Remove all devcontainer resources matching 'my-project'"
echo " $0 exec ls -la # Execute command in container"
echo " $0 prompt # Get shell prompt in container"
echo " $0 flows # List available flows"
echo " $0 flow <path> # Switch to specific Node-RED flow file"
echo " $0 -w ~/Documents # Map Documents folder to /workspace"
echo " $0 --workspace /opt/tools # Map tools folder to /workspace"
exit 0
}
# Parse command line arguments
COMMAND="env"
ENV_OPTIONS=""
# First pass: collect all options
while [ $# -gt 0 ]; do
case $1 in
-v|--version|-w|--workspace)
# Store env-specific options for later use
ENV_OPTIONS="$ENV_OPTIONS $1 $2"
shift 2
;;
--help)
show_help
;;
exec)
COMMAND="$1"
shift
# For exec, pass all remaining arguments to the exec function
break
;;
env|build|prompt|flows)
COMMAND="$1"
shift
# Don't break here, continue to collect more options
;;
flow)
COMMAND="$1"
shift
# For flow, pass any remaining arguments as flow path
break
;;
purge)
COMMAND="$1"
shift
# For purge, pass any remaining arguments as pattern
break
;;
start|stop)
COMMAND="$1"
shift
# For start/stop, pass any remaining arguments as container name
break
;;
*)
echo "Unknown option: $1"
show_help
;;
esac
done
# Execute the command
case $COMMAND in
env)
# Check SSH agent for Git operations
check_ssh_agent
generate_env
;;
build)
# Only generate env if .env file doesn't exist or is empty
if [ ! -f "$ENV_FILE" ] || [ ! -s "$ENV_FILE" ]; then
# Check SSH agent for Git operations
check_ssh_agent
generate_env
fi
build_container
;;
start)
# Only generate env if .env file doesn't exist or is empty
if [ ! -f "$ENV_FILE" ] || [ ! -s "$ENV_FILE" ]; then
# Check SSH agent for Git operations
check_ssh_agent
generate_env
fi
start_compose_profile "$@"
;;
stop)
stop_compose_profile "$@"
;;
purge)
purge_everything "$@"
;;
exec)
if [ $# -eq 0 ]; then
echo "Error: exec command requires arguments"
show_help
fi
exec_devcontainer "$@"
;;
prompt)
prompt_devcontainer
;;
flows)
list_nodered_flows
;;
flow)
if [ $# -eq 0 ]; then
echo "Error: flow command requires a flow file path"
show_help
fi
switch_nodered_flow "$1"
;;
*)
echo "Unknown command: $COMMAND"
show_help
;;
esac

View File

@@ -0,0 +1,97 @@
#!/bin/bash
# Bash completion for devrd script
# Source this file or add to your .bashrc to enable completion
_devrd_completion() {
local cur prev opts cmds
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Available commands
cmds="install env build start stop prompt purge exec flows flow"
# Available options
opts="-v --version -w --workspace --help"
# Function to get available Node-RED flows dynamically
_get_nodered_flows() {
# Get the current project name (same logic as devrd script)
local project_name="${DOCKER_COMPOSE_PROJECT_NAME:-$(basename "$(pwd)")_devcontainer}"
# Check if we're in the right directory and container is running
if [ -f "devrd" ] && docker compose -p "$project_name" -f .devcontainer/docker-compose.yml -f .devcontainer/general-devcontainer/docker-compose.devcontainer.yml ps devcontainer | grep -q "Up"; then
# Get flows from the container and return full paths (relative to workspace)
docker compose -p "$project_name" -f .devcontainer/docker-compose.yml -f .devcontainer/general-devcontainer/docker-compose.devcontainer.yml exec -T devcontainer find /workspace -name "*-flow.json" -type f 2>/dev/null | sed 's|/workspace/||' | sort
else
# Fallback to common flow file paths
echo "EVerest/config/nodered/config-sil-dc-flow.json"
echo "EVerest/config/nodered/config-sil-dc-bpt-flow.json"
echo "EVerest/config/nodered/config-sil-energy-management-flow.json"
echo "EVerest/config/nodered/config-sil-two-evse-flow.json"
echo "EVerest/config/nodered/config-sil-flow.json"
fi
}
# Function to get available container names
_get_container_names() {
echo "mqtt ocpp sil"
}
# If the previous word is an option that takes an argument, complete based on the option
case "$prev" in
-v|--version)
# Complete with common version patterns
COMPREPLY=( $(compgen -W "main master develop release/1.0 release/1.1" -- "$cur") )
return 0
;;
-w|--workspace)
# Complete directories
COMPREPLY=( $(compgen -d -- "$cur") )
return 0
;;
flow)
# Complete with available flow file paths dynamically
local flows
flows=$(_get_nodered_flows)
COMPREPLY=( $(compgen -W "$flows" -- "$cur") )
return 0
;;
start|stop)
# Complete with available container names
local containers
containers=$(_get_container_names)
COMPREPLY=( $(compgen -W "$containers" -- "$cur") )
return 0
;;
exec)
# For exec command, complete with common commands
COMPREPLY=( $(compgen -W "ls pwd cd cmake ninja make" -- "$cur") )
return 0
;;
esac
# If we're completing the first word (command), show commands
if [ $COMP_CWORD -eq 1 ]; then
COMPREPLY=( $(compgen -W "$cmds" -- "$cur") )
return 0
fi
# If we're completing an option, show options
if [[ "$cur" == -* ]]; then
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
return 0
fi
# For other cases, complete with files/directories
COMPREPLY=( $(compgen -f -- "$cur") )
return 0
}
# Register the completion function
complete -F _devrd_completion devrd
complete -F _devrd_completion ./devrd
complete -F _devrd_completion ../devrd
complete -F _devrd_completion ./applications/devrd/devrd

View File

@@ -0,0 +1,90 @@
#!/bin/zsh
# Zsh completion for devrd script
# Source this file or add to your .zshrc to enable completion
_devrd_completion() {
local context state line
typeset -A opt_args
# Available commands
local commands=(
'env:Generate .env file with repository information'
'build:Build the development container'
'start:Start containers (profiles: mqtt, ocpp, sil)'
'stop:Stop containers (profiles: mqtt, ocpp, sil)'
'purge:Remove all devcontainer resources (containers, images, volumes)'
'exec:Execute a command in the container'
'prompt:Get a shell prompt in the container'
'flows:List available flows'
'flow:Switch to specific flow file'
)
# Available options
local options=(
'-v[Everest tool branch]:version:'
'--version[Everest tool branch]:version:'
'-w[Workspace directory]:directory:_files -/'
'--workspace[Workspace directory]:directory:_files -/'
'--help[Display help message]'
)
# Function to get available Node-RED flows dynamically
_get_nodered_flows() {
# Get the current project name (same logic as devrd script)
local project_name="${DOCKER_COMPOSE_PROJECT_NAME:-$(basename "$(pwd)")_devcontainer}"
# Check if we're in the right directory and container is running
if [ -f "devrd" ] && docker compose -p "$project_name" -f .devcontainer/docker-compose.yml -f .devcontainer/general-devcontainer/docker-compose.devcontainer.yml ps devcontainer | grep -q "Up"; then
# Get flows from the container and return full paths (relative to workspace)
docker compose -p "$project_name" -f .devcontainer/docker-compose.yml -f .devcontainer/general-devcontainer/docker-compose.devcontainer.yml exec -T devcontainer find /workspace -name "*-flow.json" -type f 2>/dev/null | sed 's|/workspace/||' | sort
else
# Fallback to common flow file paths
echo "EVerest/config/nodered/config-sil-dc-flow.json"
echo "EVerest/config/nodered/config-sil-dc-bpt-flow.json"
echo "EVerest/config/nodered/config-sil-energy-management-flow.json"
echo "EVerest/config/nodered/config-sil-two-evse-flow.json"
echo "EVerest/config/nodered/config-sil-flow.json"
fi
}
# Function to get available container names
_get_container_names() {
echo "mqtt ocpp sil"
}
# Main completion logic
_arguments -C \
"$options[@]" \
"1: :{_describe 'commands' commands}" \
"*::arg:->args"
case $state in
args)
case $line[1] in
flow)
_values 'flow files' $(_get_nodered_flows)
;;
start|stop)
_values 'profiles' $(_get_container_names)
;;
exec)
_values 'commands' 'ls' 'pwd' 'cd' 'cmake' 'ninja' 'make'
;;
purge)
_files
;;
esac
;;
esac
}
# Register the completion function
if command -v compdef >/dev/null 2>&1; then
compdef _devrd_completion devrd
compdef _devrd_completion ./devrd
compdef _devrd_completion ../devrd
compdef _devrd_completion ./applications/devrd/devrd
else
echo "Warning: zsh completion system not loaded. Add 'autoload -U compinit && compinit' to your .zshrc"
fi

View File

@@ -0,0 +1,3 @@
build
__pycache__
*.egg-info

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,9 @@
[project]
name = "everest_dev_tool"
version = "0.1.0"
description = "This tool provides helpful commands to setup/control your dev environment"
license = { text="Apache-2.0" }
dependencies = []
[project.scripts]
everest = "everest_dev_tool:main"

View File

@@ -0,0 +1,9 @@
__version__ = "0.1.0"
from . import parser
def get_parser():
return parser.get_parser(__version__)
def main():
parser.main(get_parser())

View File

@@ -0,0 +1,30 @@
import argparse
import subprocess
def clone_handler(args: argparse.Namespace):
log = args.logger
log.info(
f"Cloning repository:\n"
f" Method: {args.method}\n"
f" Host: {args.host}\n"
f" SSH User (if ssh): {args.ssh_user}\n"
f" Organization: {args.organization}\n"
f" Repository Name: {args.repository_name}\n"
f" Branch: {args.branch}\n"
)
repository_url = ""
if args.method == 'https':
repository_url = f"https://{args.host}/"
else:
repository_url = f"{args.ssh_user}@{args.host}:"
repository_url = repository_url + f"{ args.organization }/{ args.repository_name }.git"
cmd_args = ["git", "clone", "-b", args.branch, repository_url]
log.debug(f"Command to execute: {' '.join(cmd_args)}")
if args.dry:
log.info(f"Dry run: Would execute: {' '.join(cmd_args)}")
else:
subprocess.run(cmd_args, check=True)

View File

@@ -0,0 +1,83 @@
import argparse
import logging
import os
from . import git_handlers
log = logging.getLogger("EVerest's Development Tool")
def get_parser(version: str) -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
description="EVerest's Development Tool",)
parser.add_argument('--version', action='version', version=f'%(prog)s { version }')
parser.add_argument('-v', '--verbose', action='store_true', help="Verbose output")
parser.set_defaults(action_handler=lambda _: parser.print_help())
subparsers = parser.add_subparsers(help="available commands")
# Git related commands
clone_parser = subparsers.add_parser("clone", help="Clone a repository", add_help=True)
clone_parser.add_argument('-v', '--verbose', action='store_true', help="Verbose output")
default_git_host = os.environ.get("EVEREST_DEV_TOOL_DEFAULT_GIT_HOST", "github.com")
clone_parser.add_argument(
'--host',
default=default_git_host,
help=(
"Git host to use, default is 'github.com' "
"(can be overridden by the environment variable "
"EVEREST_DEV_TOOL_DEFAULT_GIT_HOST)"
),
)
default_git_method = os.environ.get("EVEREST_DEV_TOOL_DEFAULT_GIT_METHOD", "ssh")
clone_parser.add_argument(
'--method',
default=default_git_method,
choices=['https', 'ssh'],
help=(
"Git method to use, default is 'ssh' "
"(can be overridden by the environment variable "
"EVEREST_DEV_TOOL_DEFAULT_GIT_METHOD)"
)
)
default_git_ssh_user = os.environ.get("EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER", "git")
clone_parser.add_argument(
'--ssh-user',
default=default_git_ssh_user,
help=(
"SSH user to use, default is 'git' "
"(can be overridden by the environment variable "
"EVEREST_DEV_TOOL_DEFAULT_GIT_SSH_USER)"
)
)
default_git_organization = os.environ.get("EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION", "EVerest")
clone_parser.add_argument(
'--organization', '--org',
default=default_git_organization,
help=(
"Github Organization name, default is 'EVerest'"
" (can be overridden by the environment variable "
"EVEREST_DEV_TOOL_DEFAULT_GIT_ORGANIZATION)"
)
)
clone_parser.add_argument('--branch', '-b', default="main", help="Branch to checkout, default is 'main'")
clone_parser.add_argument('--dry', action='store_true', help="Dry run, do not execute the clone command")
clone_parser.add_argument("repository_name", help="Name of the repository to clone")
clone_parser.set_defaults(action_handler=git_handlers.clone_handler)
return parser
def setup_logging(verbose: bool):
if verbose:
log.setLevel(logging.DEBUG)
else:
log.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
log.addHandler(console_handler)
def main(parser: argparse.ArgumentParser):
args = parser.parse_args()
args.logger = log
setup_logging(args.verbose)
args.action_handler(args)

View File

@@ -0,0 +1,73 @@
cmake_minimum_required(VERSION 3.16)
if(DEFINED EVEREST_IO_WITH_MQTT AND NOT EVEREST_IO_WITH_MQTT)
message(FATAL_ERROR "pionix_chargebridge requires MQTT support in everest::io. "
"Set EVEREST_IO_WITH_MQTT=ON or disable EVEREST_BUILD_APPLICATIONS.")
endif()
find_package(ryml QUIET)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_executable(pionix_chargebridge
src/everest_api/api_connector.cpp
src/everest_api/evse_bsp_api.cpp
src/everest_api/ovm_api.cpp
src/everest_api/ev_bsp_api.cpp
src/firmware_update/sync_fw_updater.cpp
src/utilities/filesystem.cpp
src/utilities/logging.cpp
src/utilities/parse_config.cpp
src/utilities/print_config.cpp
src/utilities/string.cpp
src/utilities/symlink.cpp
src/utilities/sync_udp_client.cpp
src/utilities/type_converters.cpp
src/can_bridge.cpp
src/charge_bridge.cpp
src/bsp_bridge.cpp
src/gpio_bridge.cpp
src/heartbeat_service.cpp
src/plc_bridge.cpp
src/serial_bridge.cpp
src/discovery.cpp
main.cpp
)
target_link_libraries(pionix_chargebridge
everest::io
everest::everest_api_types
nlohmann_json::nlohmann_json
ryml::ryml
)
target_include_directories(pionix_chargebridge
PRIVATE include
PRIVATE shared
)
set(cb_firmware_binary config/firmware/charge-bridge-fw_complete.cbfw)
add_custom_command(
TARGET pionix_chargebridge
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/${cb_firmware_binary}"
"$<TARGET_FILE_DIR:pionix_chargebridge>/"
COMMENT "Copying Pionix ChargeBridge firmware binary..."
)
install (TARGETS pionix_chargebridge)
install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/${cb_firmware_binary}" DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/chargebridge/firmware)
install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/config/config-CB-EVAL.yaml" DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/chargebridge RENAME "config-CB-EVAL.yaml-example")
install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/config/config-CB-SAT-AC.yaml" DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/chargebridge RENAME "config-CB-SAT-AC.yaml-example")
install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/config/config-CB-EVAL-EV.yaml" DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/chargebridge RENAME "config-CB-EVAL-EV.yaml-example")
install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/config/config-CB-EVAL-SIM.yaml" DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/chargebridge RENAME "config-CB-EVAL-SIM.yaml-example")

View File

@@ -0,0 +1,154 @@
charge_bridge:
name: cb_eval_ev
ip: ANY_EV
#ip: chargebridge-44b7d0c99629.local
fw_file: ./firmware/charge-bridge-fw_complete.cbfw
fw_update_on_start: true
mdns_name: ""
heartbeat:
interval_s: 1
connection_to_s: 10
safety:
pp_mode: "disabled"
cp_avg_ms: 10
inverted_emergency_input: 0
relay_1:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 200
feedback_inverted: true
# PWM not supported yet
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_2:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 200
feedback_inverted: true
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_3:
relay_mode: "UserRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: false
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
can_0:
enable: true
local: "cb_ev_can"
baudrate: 125000
serial_1:
enable: true
local: "/dev/cb_ev_uart"
baudrate: 115200
stopbits: OneStopBit
parity: None
serial_2:
enable: true
local: "/dev/cb_ev_rs485"
baudrate: 19200
stopbits: OneStopBit
parity: Even
plc:
enable: true
tap: "cb_ev_plc"
ip: 172.25.6.1
netmask: 255.255.255.0
mtu: 1518
powersaving_mode: 1
ev_bsp:
enable: true
module_id: "ev_bsp_1"
mqtt_remote: 127.0.0.1
mqtt_port: 1883
mqtt_bind: 127.0.0.1
ovm_enabled: false
ovm_module_id: "ovm_1"
evse_bsp:
enable: false
module_id: "bsp_1"
mqtt_remote: 127.0.0.1
mqtt_port: 1883
mqtt_bind: 127.0.0.1
capabilities:
max_current_A_import: 16
min_current_A_import: 6
max_phase_count_import: 3
min_phase_count_import: 3
max_current_A_export: 16
min_current_A_export: 6
max_phase_count_export: 3
min_phase_count_export: 3
supports_changing_phases_during_charging: false
connector_type: "IEC62196Type2Cable"
max_plug_temperature_C: 250
ovm_enabled: true
ovm_module_id: "ovm_1"
gpio:
enable: true
interval_s: 1
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_ping_interval_ms: 5000
gpio_0:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_1:
mode: "Input"
pulls: "NoPull"
config: 32767
gpio_2:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_3:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_4:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 1000
gpio_5:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_6:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_7:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_8:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_9:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767

View File

@@ -0,0 +1,153 @@
charge_bridge:
name: cb_eval
ip: ANY_EVSE
#ip: chargebridge-44b7d0c99629.local
fw_file: ./firmware/charge-bridge-fw_complete.cbfw
fw_update_on_start: true
mdns_name: ""
heartbeat:
interval_s: 1
connection_to_s: 10
safety:
pp_mode: "disabled"
cp_avg_ms: 10
inverted_emergency_input: 0
relay_1:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 200
feedback_inverted: true
# PWM not supported yet
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_2:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 200
feedback_inverted: true
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_3:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: true
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
can_0:
enable: true
local: "cb_can"
baudrate: 125000
serial_1:
enable: true
local: "/dev/cb_uart"
baudrate: 115200
stopbits: OneStopBit
parity: None
serial_2:
enable: true
local: "/dev/cb_rs485"
baudrate: 19200
stopbits: OneStopBit
parity: Even
plc:
enable: true
tap: "cb_plc"
ip: 172.25.6.1
netmask: 255.255.255.0
mtu: 1518
powersaving_mode: 1
ev_bsp:
enable: false
module_id: "ev_bsp_1"
mqtt_remote: 127.0.0.1
mqtt_port: 1883
mqtt_bind: 127.0.0.1
ovm_enabled: false
ovm_module_id: "ovm_1"
evse_bsp:
enable: true
module_id: "cb_bsp"
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_ping_interval_ms: 5000
ovm_enabled: true
ovm_module_id: "cb_ovm"
capabilities:
max_current_A_import: 16
min_current_A_import: 0
max_phase_count_import: 3
min_phase_count_import: 3
max_current_A_export: 16
min_current_A_export: 0
max_phase_count_export: 3
min_phase_count_export: 3
supports_changing_phases_during_charging: false
connector_type: "IEC62196Type2Cable"
gpio:
enable: true
interval_s: 1
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_ping_interval_ms: 5000
gpio_0:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_1:
mode: "Input"
pulls: "NoPull"
config: 32767
gpio_2:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_3:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_4:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 1000
gpio_5:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_6:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_7:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_8:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_9:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767

View File

@@ -0,0 +1,147 @@
charge_bridge:
name: cb_eval
ip: ANY_EVSE
#ip: chargebridge-44b7d0c9bcc0.local^
#ip: chargebridge-44b7d0c99629.local
fw_file: ./firmware/charge-bridge-fw_complete.cbfw
fw_update_on_start: true
mdns_name: ""
heartbeat:
interval_s: 1
connection_to_s: 10
safety:
pp_mode: "disabled"
cp_avg_ms: 10
inverted_emergency_input: 0
relay_1:
relay_mode: "PowerRelay"
feedback_enabled: true
feedback_delay_ms: 200
feedback_inverted: true
# PWM not supported yet
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_2:
relay_mode: "PowerRelay"
feedback_enabled: true
feedback_delay_ms: 200
feedback_inverted: true
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_3:
relay_mode: "UserRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: false
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
can_0:
enable: true
local: "cb_can"
baudrate: 125000
serial_1:
enable: true
local: "/dev/cb_uart"
baudrate: 115200
stopbits: OneStopBit
parity: None
serial_2:
enable: true
local: "/dev/cb_rs485"
baudrate: 19200
stopbits: OneStopBit
parity: Even
plc:
enable: true
tap: "cb_plc"
ip: 172.25.6.2
netmask: 255.255.255.0
mtu: 1518
powersaving_mode: 1
evse_bsp:
enable: true
module_id: "cb_bsp"
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_bind: 127.0.0.1
mqtt_ping_interval_ms: 5000
ovm_enabled: true
ovm_module_id: "cb_ovm"
capabilities:
max_current_A_import: 16
min_current_A_import: 0
max_phase_count_import: 3
min_phase_count_import: 3
max_current_A_export: 16
min_current_A_export: 0
max_phase_count_export: 3
min_phase_count_export: 3
supports_changing_phases_during_charging: false
connector_type: "IEC62196Type2Cable"
gpio:
enable: true
interval_s: 1
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_bind: 127.0.0.1
mqtt_ping_interval_ms: 5000
gpio_0:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_1:
mode: "Input"
pulls: "NoPull"
config: 32767
gpio_2:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_3:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_4:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 1000
gpio_5:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_6:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_7:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_8:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_9:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767

View File

@@ -0,0 +1,169 @@
charge_bridge:
name: cb_sat_ac
ip: ANY_EVSE
fw_file: ./firmware/charge-bridge-fw_complete.cbfw
fw_update_on_start: true
mdns_name: ""
heartbeat:
interval_s: 1
connection_to_s: 10
safety:
pp_mode: "disabled"
cp_avg_ms: 10
inverted_emergency_input: 0
relay_1:
relay_mode: "PowerRelay"
# Auxilary contact is connected from the OMRON relay
feedback_enabled: true
# The Omron relay switches in less than 100ms, use 200ms here
feedback_delay_ms: 200
# Only for PCB version 1.1, set to false for PCB version 1.2 and up
feedback_inverted: true
# PWM not supported yet
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_2:
# Not connected on PCB
relay_mode: "UserRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: false
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_3:
# Not connected on PCB
relay_mode: "UserRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: false
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
can_0:
enable: true
local: "cb_can"
baudrate: 250000
serial_1:
enable: true
local: "/dev/cb_uart"
baudrate: 115200
stopbits: OneStopBit
parity: None
serial_2:
enable: true
local: "/dev/cb_rs485"
baudrate: 9600
stopbits: OneStopBit
parity: None
plc:
enable: true
tap: "cb_plc"
ip: 172.25.6.1
netmask: 255.255.255.0
mtu: 1518
powersaving_mode: 1
evse_bsp:
enable: true
module_id: "cb_bsp"
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_bind: 127.0.0.1
mqtt_ping_interval_ms: 1000
capabilities:
max_current_A_import: 16
min_current_A_import: 6
max_phase_count_import: 3
min_phase_count_import: 3
max_current_A_export: 16
min_current_A_export: 6
max_phase_count_export: 3
min_phase_count_export: 3
supports_changing_phases_during_charging: false
connector_type: "IEC62196Type2Cable"
ovm_enabled: false
ovm_module_id: cb_ovm
gpio:
enable: true
interval_s: 1
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_bind: 127.0.0.1
mqtt_ping_interval_ms: 1000
gpio_0:
# RCD.TEST
#mode: "Rcd_Selftest_Output"
# Self test not fully supported yet
mode: "Input"
pulls: "NoPull"
mdns: false
# RCD self test duration (ignore emergency input signals for this time after self test)
# and show reason of safety decision on host somehow to simplify debugging
config: 1000
gpio_1:
# RCD.ERROR
mode: "Input"
pulls: "NoPull"
mdns: false
config: 0
gpio_2:
# MOTOR_1
# simple Motor lock with only 2 wires (no feedback contacts)
mode: "MotorLock_1"
pulls: "NoPull"
mdns: false
# 1000 ms motor drive time for locking/unlocking
config: 1000
gpio_3:
# MOTOR_2
# simple Motor lock with only 2 wires (no feedback contacts)
mode: "MotorLock_2"
pulls: "NoPull"
mdns: false
config: 1000
gpio_4:
# RCD.PWM
# not supported yet
mode: "Input"
pulls: "NoPull"
mdns: false
config: 0
gpio_5:
# External GPIO on connector J4 pin 9 (10kOhm I/O)
mode: "Input"
pulls: "NoPull"
mdns: false
config: 0
gpio_6:
# External GPIO on connector J4 pin 10 (10kOhm I/O)
mode: "Input"
pulls: "PullUp"
mdns: false
config: 0
gpio_7:
# External GPIO on connector J3 pin 11 (10kOhm I/O)
mode: "Input"
pulls: "NoPull"
mdns: false
config: 0
gpio_8:
# External GPIO on connector J3 pin 12 (10kOhm I/O)
mode: "Input"
pulls: "PullDown"
mdns: false
config: 0
gpio_9:
# Not connected
mode: "Input"
pulls: "PullDown"
mdns: false
config: 0

View File

@@ -0,0 +1,143 @@
charge_bridge_ip_list : [ "192.168.188.65", "192.168.188.65"]
charge_bridge:
name: cb_##
ip: ""
fw_file: ./firmware/charge-bridge-fw_complete.cbfw
fw_update_on_start: false
mdns_name: "pionix_cb_##"
heartbeat:
interval_s: 1
connection_to_s: 10
safety:
pp_mode: "disabled"
cp_avg_ms: 10
relay_1:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: false
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_2:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: false
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
relay_3:
relay_mode: "PowerRelay"
feedback_enabled: false
feedback_delay_ms: 10
feedback_inverted: false
pwm_dc: 100
pwm_delay_ms: 0
switchoff_delay_ms: 10
can_0:
enable: true
local: "cb_##_can0"
baudrate: 250000
serial_1:
enable: true
local: "/dev/cb_##_serial_1"
baudrate: 19200
stopbits: OneStopBit
parity: None
serial_2:
enable: false
local: "/dev/cb_##_serial_2"
baudrate: 19200
stopbits: OneStopBit
parity: None
plc:
enable: false
tap: "cb_##_tap0"
ip: 172.25.6.1
netmask: 255.255.255.0
mtu: 1518
powersaving_mode: 1
evse_bsp:
enable: false
module_id: "bsp_##"
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_ping_interval_ms: 1000
capabilities:
max_current_A_import: 16
min_current_A_import: 6
max_phase_count_import: 3
min_phase_count_import: 3
max_current_A_export: 16
min_current_A_export: 6
max_phase_count_export: 3
min_phase_count_export: 3
supports_changing_phases_during_charging: false
connector_type: "IEC62196Type2Cable"
ovm_enabled: true
ovm_module_id: "ovm_1"
gpio:
enable: false
interval_s: 4
mqtt_remote: "localhost"
mqtt_port: 1883
mqtt_ping_interval_ms: 1000
gpio_0:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_1:
mode: "Input"
pulls: "NoPull"
config: 32767
gpio_2:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_3:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767
gpio_4:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_5:
mode: "Output"
pulls: "NoPull"
mdns: false
config: 32767
gpio_6:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_7:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_8:
mode: "Output"
pulls: "PullUp"
mdns: false
config: 32767
gpio_9:
mode: "Input"
pulls: "PullUp"
mdns: false
config: 32767

View File

@@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/everest_api/api_connector.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/udp/udp_client.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
namespace charge_bridge {
struct bsp_bridge_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
evse_bsp::everest_api_config api;
};
class bsp_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
bsp_bridge(bsp_bridge_config const& config);
~bsp_bridge() = default;
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_timer_event();
evse_bsp::api_connector m_api;
everest::lib::io::udp::udp_client m_udp;
everest::lib::io::event::timer_fd m_timer;
bool m_udp_on_error{false};
};
} // namespace charge_bridge

View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <everest/io/can/can_payload.hpp>
#include <everest/io/can/socket_can.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/udp/udp_client.hpp>
#include <memory>
extern "C" struct cb_can_message;
namespace charge_bridge {
struct can_bridge_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
std::string can_device;
};
class can_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
can_bridge(can_bridge_config const& config);
~can_bridge();
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_error_timer();
void handle_heartbeat_timer();
void send_can_to_udp(cb_can_message const& pl);
std::unique_ptr<everest::lib::io::can::socket_can> m_can;
everest::lib::io::udp::udp_client m_udp;
std::string m_can_device;
std::string m_identifier;
everest::lib::io::event::timer_fd m_heartbeat_timer;
std::chrono::steady_clock::time_point m_last_msg_to_cb;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,90 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <atomic>
#include <charge_bridge/bsp_bridge.hpp>
#include <charge_bridge/can_bridge.hpp>
#include <charge_bridge/discovery.hpp>
#include <charge_bridge/firmware_update/sync_fw_updater.hpp>
#include <charge_bridge/gpio_bridge.hpp>
#include <charge_bridge/heartbeat_service.hpp>
#include <charge_bridge/plc_bridge.hpp>
#include <charge_bridge/serial_bridge.hpp>
#include <charge_bridge/utilities/symlink.hpp>
#include <everest/io/event/fd_event_handler.hpp>
#include <everest/io/serial/event_pty.hpp>
#include <everest/io/tun_tap/tap_client.hpp>
#include <everest/util/async/monitor.hpp>
#include <memory>
#include <optional>
namespace charge_bridge {
struct charge_bridge_status {
bool is_connected{false};
bool discovery_pending{false};
};
struct charge_bridge_config {
std::string cb_name;
std::uint16_t cb_port;
std::string cb_remote;
std::optional<can_bridge_config> can0;
std::optional<serial_bridge_config> serial1;
std::optional<serial_bridge_config> serial2;
std::optional<serial_bridge_config> serial3;
std::optional<plc_bridge_config> plc;
std::optional<bsp_bridge_config> bsp;
std::optional<heartbeat_config> heartbeat;
std::optional<gpio_config> gpio;
firmware_update::fw_update_config firmware;
};
void print_charge_bridge_config(charge_bridge_config const& config);
class charge_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
charge_bridge(charge_bridge_config const& config);
~charge_bridge();
bool update_firmware(bool force);
std::string get_pty_1_slave_path();
std::string get_pty_2_slave_path();
std::string get_pty_3_slave_path();
void print_config();
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void manage(everest::lib::io::event::fd_event_handler& handler, std::atomic_bool const& exit, bool force_update);
private:
void init();
void init_discovery(discovery_device_type type, std::set<std::string> const& interfaces, bool excluding);
void handle_discovery(std::string const& ip);
private:
std::unique_ptr<can_bridge> m_can_0_client;
std::unique_ptr<serial_bridge> m_pty_1;
std::unique_ptr<serial_bridge> m_pty_2;
std::unique_ptr<serial_bridge> m_pty_3;
std::unique_ptr<bsp_bridge> m_bsp;
std::unique_ptr<plc_bridge> m_plc;
std::unique_ptr<heartbeat_service> m_heartbeat;
std::unique_ptr<gpio_bridge> m_gpio;
std::unique_ptr<discovery> m_discovery;
everest::lib::io::event::fd_event_handler* m_event_handler{nullptr};
bool m_force_firmware_update{false};
everest::lib::util::monitor<charge_bridge_status> m_cb_status;
bool m_was_connected{false};
bool m_discovery_active{false};
charge_bridge_config m_config;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/mdns/mdns.hpp>
#include <everest/io/mdns/mdns_client.hpp>
#include <functional>
#include <memory>
#include <set>
namespace charge_bridge {
enum class discovery_device_type {
CB_EVSE,
CB_EV
};
class discovery : public everest::lib::io::event::fd_event_register_interface {
public:
using discovery_cb = std::function<void(std::string const&)>;
discovery(discovery_device_type type);
discovery(discovery_device_type type, std::set<std::string> const& interfaces, bool excluding);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void set_discovery_callback(discovery_cb const& cb);
private:
void add_client(std::string const& interface);
void query_registry();
std::vector<std::unique_ptr<everest::lib::io::mdns::mdns_client>> m_mdns;
everest::lib::io::event::timer_fd m_timer;
discovery_cb m_on_discover;
everest::lib::io::mdns::mDNS_registry m_registry;
discovery_device_type m_type;
static const std::string discovery_id;
};
} // namespace charge_bridge

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/everest_api/ev_bsp_api.hpp>
#include <charge_bridge/everest_api/evse_bsp_api.hpp>
#include <charge_bridge/everest_api/ovm_api.hpp>
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/mqtt/mqtt_client.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_BSP = everest::lib::API::V1_0::types::evse_board_support;
struct everest_api_config {
std::string mqtt_remote;
std::string mqtt_bind;
uint16_t mqtt_port;
uint32_t mqtt_ping_interval_ms;
evse_bsp_config evse;
evse_ovm_config ovm;
evse_ev_bsp_config ev;
};
class api_connector : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
public:
api_connector(everest_api_config const& config, std::string const& cb_identifier);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_mqtt_connect();
void handle_cb_connection_state();
bool check_cb_heartbeat();
std::string m_cb_identifier;
everest::lib::io::mqtt::mqtt_client m_mqtt;
tx_ftor m_tx;
std::chrono::steady_clock::time_point m_last_cb_heartbeat;
everest::lib::io::event::timer_fd m_sync_timer;
std::string m_evse_bsp_receive_topic;
std::string m_evse_bsp_send_topic;
std::string m_ovm_receive_topic;
std::string m_ovm_send_topic;
std::string m_ev_bsp_receive_topic;
std::string m_ev_bsp_send_topic;
bool m_evse_bsp_enabled{false};
bool m_ovm_enabled{false};
bool m_ev_bsp_enabled{false};
bool m_cb_initial_comm_check{true};
bool m_cb_connected{false};
evse_bsp_host_to_cb m_host_status;
evse_bsp_api m_evse_bsp;
ovm_api m_ovm;
ev_bsp_api m_ev_bsp;
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,101 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest_api_types/ev_board_support/API.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/generic/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_EVSE_BSP = everest::lib::API::V1_0::types::evse_board_support;
namespace API_EV_BSP = everest::lib::API::V1_0::types::ev_board_support;
namespace API_EVM = everest::lib::API::V1_0::types::evse_manager;
namespace API_GENERIC = everest::lib::API::V1_0::types::generic;
// namespace API_OVM = everest::lib::API::V1_0::types::over_voltage_monitor;
struct evse_ev_bsp_config {
bool enabled{false};
std::string module_id;
};
class ev_bsp_api : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
using mqtt_ftor = std::function<void(std::string const&, std::string const&)>;
public:
ev_bsp_api(evse_ev_bsp_config const& config, std::string const& cb_identifier, evse_bsp_host_to_cb& host_status);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
void set_mqtt_tx(mqtt_ftor const& tx);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void dispatch(std::string const& operation, std::string const& payload);
void raise_comm_fault();
void clear_comm_fault();
void sync(bool cb_connected);
private:
void tx(evse_bsp_host_to_cb const& msg);
void send_bsp_event(API_EVSE_BSP::Event data);
void send_bsp_measurement(API_EV_BSP::BspMeasurement data);
void send_ev_info(API_EVM::EVInfo data);
void send_raise_error(API_GENERIC::ErrorEnum error, std::string const& subtype, std::string const& msg);
void send_clear_error(API_GENERIC::ErrorEnum error, std::string const& subtype);
void send_communication_check();
void send_mqtt(std::string const& topic, std::string const& message);
void send_event(API_EVSE_BSP::Event data);
void receive_enable(std::string const& payload);
void receive_set_cp_state(std::string const& payload);
void receive_allow_power_on(std::string const& payload);
void receive_diode_fail(std::string const& payload);
void receive_set_ac_max_current(std::string const& payload);
void receive_set_three_phases(std::string const& payload);
void receive_set_rcd_error(std::string const& payload);
void receive_heartbeat(std::string const& pl);
void handle_error(const SafetyErrorFlags& data);
void handle_event_cp(std::uint8_t cp);
void handle_event_relay(std::uint8_t relay);
void handle_bsp_measurement(uint16_t cp, uint8_t pp_1, uint8_t pp2);
bool check_everest_heartbeat();
void handle_everest_connection_state();
evse_bsp_host_to_cb& host_status;
evse_bsp_cb_to_host m_cb_status;
tx_ftor m_tx;
bool m_everest_connected{false};
bool m_cb_connected{false};
bool m_cb_initial_comm_check{true};
bool m_bc_initial_comm_check{true};
std::string m_cb_identifier;
std::chrono::steady_clock::time_point last_everest_heartbeat;
mqtt_ftor m_mqtt_tx;
std::size_t m_last_hb_id{0};
everest::lib::API::V1_0::types::evse_board_support::Event last_cp_event{
everest::lib::API::V1_0::types::evse_board_support::Event::Disconnected};
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,103 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest_api_types/evse_board_support/API.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_BSP = everest::lib::API::V1_0::types::evse_board_support;
namespace API_EVM = everest::lib::API::V1_0::types::evse_manager;
struct evse_bsp_config {
std::string module_id;
bool enabled{false};
API_BSP::HardwareCapabilities capabilities;
};
class evse_bsp_api : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
using mqtt_ftor = std::function<void(std::string const&, std::string const&)>;
public:
evse_bsp_api(evse_bsp_config const& config, std::string const& cb_identifier, evse_bsp_host_to_cb& host_status);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
void set_mqtt_tx(mqtt_ftor const& tx);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void dispatch(std::string const& operation, std::string const& payload);
void raise_comm_fault();
void clear_comm_fault();
void sync(bool cb_connected);
private:
void tx(evse_bsp_host_to_cb const& msg);
void handle_event_cp(std::uint8_t cp);
void handle_event_relay(std::uint8_t relay);
void handle_error(const SafetyErrorFlags& data);
void handle_pp_type1(std::uint8_t data);
void handle_pp_type2(std::uint8_t data);
void handle_stop_button(std::uint8_t data);
void send_event(API_BSP::Event data);
void send_ac_nr_of_phases(std::uint8_t data);
void send_capabilities();
void send_ac_pp_amapcity(API_BSP::Ampacity data);
void send_request_stop_transaction(API_EVM::StopTransactionReason data);
void send_rcd_current(std::uint8_t data);
void send_raise_error(API_BSP::ErrorEnum error, std::string const& subtype, std::string const& msg);
void send_clear_error(API_BSP::ErrorEnum error, std::string const& subtype, std::string const& msg);
void send_communication_check();
void send_reply_reset(std::string const& replyTo);
void send_mqtt(std::string const& topic, std::string const& message);
void receive_enable(std::string const& payload);
void receive_pwm_on(std::string const& payload);
void receive_cp_state_X1(std::string const& payload);
void receive_cp_state_F(std::string const& payload);
void receive_allow_power_on(std::string const& payload);
void receive_ac_switch_three_phases_while_charging(std::string const& payload);
void receive_ac_overcurrent_limit(std::string const& payload);
void receive_lock();
void receive_unlock();
void receive_self_test(std::string const& payload);
void receive_request_reset(std::string const& payload);
void receive_heartbeat(std::string const& pl);
bool check_everest_heartbeat();
void handle_everest_connection_state();
evse_bsp_host_to_cb& host_status;
evse_bsp_cb_to_host cb_status;
tx_ftor m_tx;
everest::lib::io::event::timer_fd m_capabilities_timer;
API_BSP::HardwareCapabilities m_capabilities;
bool m_enabled{false};
bool everest_connected{false};
bool m_cb_connected{false};
bool m_bc_initial_comm_check{true};
std::string m_cb_identifier;
std::chrono::steady_clock::time_point last_everest_heartbeat;
mqtt_ftor m_mqtt_tx;
std::size_t m_last_hb_id{0};
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,85 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest_api_types/evse_manager/API.hpp>
#include <everest_api_types/over_voltage_monitor/API.hpp>
#include <everest_api_types/utilities/Topics.hpp>
#include <functional>
#include <protocol/cb_common.h>
#include <protocol/evse_bsp_cb_to_host.h>
#include <protocol/evse_bsp_host_to_cb.h>
#include <string>
namespace charge_bridge::evse_bsp {
namespace API_OVM = everest::lib::API::V1_0::types::over_voltage_monitor;
struct evse_ovm_config {
bool enabled{false};
std::string module_id;
};
class ovm_api : public everest::lib::io::event::fd_event_register_interface {
using tx_ftor = std::function<void(evse_bsp_host_to_cb const&)>;
using rx_ftor = std::function<void(evse_bsp_cb_to_host const&)>;
using mqtt_ftor = std::function<void(std::string const&, std::string const&)>;
public:
ovm_api(evse_ovm_config const& config, std::string const& cb_identifier, evse_bsp_host_to_cb& host_status);
void set_cb_tx(tx_ftor const& handler);
void set_cb_message(evse_bsp_cb_to_host const& msg);
void set_mqtt_tx(mqtt_ftor const& tx);
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
void dispatch(std::string const& operation, std::string const& payload);
void raise_comm_fault();
void clear_comm_fault();
void sync(bool cb_connected);
private:
void tx(evse_bsp_host_to_cb const& msg);
void send_voltage_measurement_V(double data);
void send_raise_error(API_OVM::ErrorEnum error, std::string const& subtype, std::string const& msg,
API_OVM::ErrorSeverityEnum severity);
void send_clear_error(API_OVM::ErrorEnum error, std::string const& subtype);
void send_communication_check();
void send_mqtt(std::string const& topic, std::string const& message);
void handle_dc_hv_ov_emergency(bool high);
void handle_dc_hv_ov_error(bool high);
void handle_cp_state(CpState state);
void receive_set_limits(std::string const& payload);
void receive_start();
void receive_stop();
void receive_reset_over_voltage_error();
void receive_heartbeat(std::string const& pl);
bool check_everest_heartbeat();
void handle_everest_connection_state();
evse_bsp_host_to_cb& host_status;
evse_bsp_cb_to_host m_cb_status;
tx_ftor m_tx;
bool m_everest_connected{false};
bool m_cb_connected{false};
bool m_cb_initial_comm_check{true};
bool m_bc_initial_comm_check{true};
std::string m_cb_identifier;
std::chrono::steady_clock::time_point last_everest_heartbeat;
API_OVM::OverVoltageLimits m_limits{0, 0};
mqtt_ftor m_mqtt_tx;
std::size_t m_last_hb_id{0};
};
} // namespace charge_bridge::evse_bsp

View File

@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <charge_bridge/utilities/filesystem.hpp>
#include <charge_bridge/utilities/sync_udp_client.hpp>
#include <fstream>
namespace charge_bridge::firmware_update {
struct fw_update_config {
std::string cb;
std::uint16_t cb_port;
std::string cb_remote;
std::string fw_path;
bool fw_update_on_start;
};
class sync_fw_updater {
public:
sync_fw_updater(fw_update_config const& config);
~sync_fw_updater() = default;
std::optional<std::string> get_fw_version();
bool switch_bank();
bool ping();
bool upload_fw();
void print_fw_version();
bool print_switch_bank();
bool quick_check_connection();
bool check_connection();
bool check_if_correct_fw_installed();
private:
bool check_reply(utilities::sync_udp_client::reply const& val);
bool upload_firmware();
bool upload_init(const fs::path& file_path, std::uint32_t& offset,
charge_bridge::filesystem_utils::CryptSignedHeader& hdr);
bool upload_transfer(const fs::path& file_path, std::uint16_t& sector, std::uint32_t offset,
std::uint32_t& total_bytes);
bool upload_finish(const fs::path& file_path, std::uint32_t total_bytes,
const charge_bridge::filesystem_utils::CryptSignedHeader& hdr);
everest::lib::io::udp::udp_payload make_fw_chunk(std::uint16_t sector, std::uint8_t last_chunk,
std::vector<std::uint8_t> const& data);
utilities::sync_udp_client m_udp;
fw_update_config m_config;
static const std::uint32_t app_udp_sector_size;
static const std::uint16_t sub_chunk_size;
};
} // namespace charge_bridge::firmware_update

View File

@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include "everest/io/mqtt/mosquitto_cpp.hpp"
#include <array>
#include <everest/io/can/can_payload.hpp>
#include <everest/io/event/fd_event_register_interface.hpp>
#include <everest/io/event/timer_fd.hpp>
#include <everest/io/mqtt/mqtt_client.hpp>
#include <everest/io/udp/udp_client.hpp>
#include <protocol/cb_management.h>
namespace charge_bridge {
struct gpio_config {
std::string cb;
std::string item;
std::uint16_t cb_port;
std::string cb_remote;
std::uint16_t interval_s;
std::string mqtt_remote;
std::string mqtt_bind;
std::uint16_t mqtt_port;
std::uint32_t mqtt_ping_interval_ms;
};
class gpio_bridge : public everest::lib::io::event::fd_event_register_interface {
public:
gpio_bridge(gpio_config const& config);
~gpio_bridge();
bool register_events(everest::lib::io::event::fd_event_handler& handler) override;
bool unregister_events(everest::lib::io::event::fd_event_handler& handler) override;
private:
void handle_error_timer();
void handle_heartbeat_timer();
void handle_udp_rx(everest::lib::io::udp::udp_payload const& payload);
void dispatch(everest::lib::io::mqtt::mqtt_client::message const& data);
void send_mqtt(std::string const& topic, std::string const& message);
void send_udp();
everest::lib::io::udp::udp_client m_udp;
bool m_udp_on_error{false};
everest::lib::io::event::timer_fd m_heartbeat_timer;
std::chrono::steady_clock::time_point last_heartbeat;
CbManagementPacket<CbGpioPacket> m_message;
std::string m_identifier;
bool m_mqtt_on_error{false};
everest::lib::io::mqtt::mqtt_client m_mqtt;
std::string m_receive_topic;
std::string m_send_topic;
};
} // namespace charge_bridge

Some files were not shown because too many files have changed in this diff Show More