NEWS
IOBroker Automatisiertes Deployment
-
@vippis sagte in IOBroker Automatisiertes Deployment:
Habe zu den meisten Punkten von ChatGPT gefragt, aber der stößt an seine Grenzen.
Bedanke mich recht herzlich für Eure Hilfe!
Einen Teil der Anforderungen habe ich mit einem userscript umgesetzt, welches beim ersten Start des Containers automatisch ausgeführt wird. Dazu wird z.B. ein Scriptverzeichnis in den Container gemappt
volumes: - /opt/docker/iobroker2:/opt/iobroker - /opt/docker/userscripts/test:/opt/userscripts
Das Script muss "userscript_firststart.sh" heißen. Mein Script sieht dann so aus (weil es ein testsytem ist, Sentry abgeschaltet). Das lässt sich sicher noch um einige deiner Punkte ergänzen
#!/bin/bash # To run the Script on the first start of a new container you have to rename it to userscript_firststart.sh. echo " " echo "Checking if Discovery Adapter is installed..." # Check if the Discovery Adapter is installed if iob list adapters | grep -q "discovery"; then echo "Deleting Discovery Adapter ..." iob del discovery else echo "Discovery Adapter is not installed." fi echo " " # Define variables for system.config parameters LICENSE_CONFIRMED=true SITE_NAME="TEST +++++++ TEST +++++++ TEST +++++++ TEST" DIAG="none" REPO="beta" LANGUAGE="en" CITY="Test" COUNTRY="Germany" LONGITUDE="10.0000" LATITUDE="52.0000" echo "Confirm license ..." current_license=$(iob object get system.config | jq -r '.common.licenseConfirmed') if [ "$current_license" != "$LICENSE_CONFIRMED" ]; then iob object set system.config common.licenseConfirmed=$LICENSE_CONFIRMED fi echo "Setting Site Name ..." current_siteName=$(iob object get system.config | jq -r '.common.siteName') if [ "$current_siteName" != "$SITE_NAME" ]; then iob object set system.config common.siteName="$SITE_NAME" fi echo "Setting Diag Data ..." current_diag=$(iob object get system.config | jq -r '.common.diag') if [ "$current_diag" != "$DIAG" ]; then iob object set system.config common.diag="$DIAG" fi echo "Setting Repo ..." current_repo=$(iob object get system.config | jq -r '.common.activeRepo') if [ "$current_repo" != "$REPO" ]; then iob object set system.config common.activeRepo="$REPO" fi echo "Setting Language ..." current_language=$(iob object get system.config | jq -r '.common.language') if [ "$current_language" != "$LANGUAGE" ]; then iob object set system.config common.language="$LANGUAGE" fi echo "Setting miscellaneous ..." current_city=$(iob object get system.config | jq -r '.common.city') if [ "$current_city" != "$CITY" ]; then iob object set system.config common.city="$CITY" fi current_country=$(iob object get system.config | jq -r '.common.country') if [ "$current_country" != "$COUNTRY" ]; then iob object set system.config common.country="$COUNTRY" fi current_longitude=$(iob object get system.config | jq -r '.common.longitude') if [ "$current_longitude" != "$LONGITUDE" ]; then iob object set system.config common.longitude="$LONGITUDE" fi current_latitude=$(iob object get system.config | jq -r '.common.latitude') if [ "$current_latitude" != "$LATITUDE" ]; then iob object set system.config common.latitude="$LATITUDE" fi echo " " echo "Disable Sentry ..." iob plugin disable sentry echo " " exit 0
-
ein paar der punkte kannst du aus dem dev-server nehmen
https://github.com/ioBroker/dev-server/blob/93790110fa07cf60da42bfd97cbf51c6dc75b6f9/src/index.ts#L1352da wird für die entwicklung eine iobroker instanz erstellt mit bestimmten voreinstellungen.
vieles findest du im datenpunkt
system.config -
Vielen Dank schonmal @OliverIO und @Marc-Berg
Die härteste Nuss finde ich fast das Admin PW setzen.
Im Fall von Docker kann man ja
docker exec iobroker bash -lc "iobroker user passwd '${IOB_ADMIN_USER}' '${IOB_ADMIN_PASSWORD}'"
ausführen, aber ich lande immer in einer User-Eingabe das Passwort zu setzen. Geht das nicht per Skript?
Und die NPM Pakete für den JS Adapter. Habt ihr da auch ein Kommando?
-
wenn du solche dinge machen willst, wirst du dich mit den tieferen ebenen des iobrokers beschäftigen müssen
https://github.com/ioBroker/ioBroker.js-controller/blob/68fdbe2f4c4f0bef8b50f5e3b534363ebe77a15d/packages/common-db/src/lib/common/password.tskeine ahnung ob das angegebene beispiel auch genau so funktioniert und gültige passwörter für die aktuelle instanz ausspuckt, die man dann schreiben kann
-
@vippis sagte in IOBroker Automatisiertes Deployment:
docker exec iobroker bash -lc "iobroker user passwd '${IOB_ADMIN_USER}' '${IOB_ADMIN_PASSWORD}'"
docker exec -it iobroker2 bash -c 'printf "dein_neues_passwort\ndein_neues_passwort\n" | iobroker user passwd admin' Enter your password: Repeat your password: Password for "admin" was successfully set.
So kann man das Passwort automatisch setzen. (ohne Abfrage)
-
Für die Installation der NPM Module:
#!/bin/bash log() { echo -e "[INFO] $1"; } IOBROKER_CID="iobroker" IOBROKER_NPM_PACKAGES=("moment" "axios" "influxdb-client" "@influxdata/influxdb-client" "@influxdata/influxdb-client-apis") # Array in Leerzeichen-getrennten String umwandeln libs_string=$(printf "%s " "${IOBROKER_NPM_PACKAGES[@]}") libs_string="${libs_string%" "}" # letztes Leerzeichen entfernen log "➡️ Setze libraries für javascript.0 auf: ${libs_string}" docker exec -u 0 "${IOBROKER_CID}" bash -c \ "iobroker object extend system.adapter.javascript.0 '{\"native\":{\"libraries\":\"${libs_string}\"}}'" # Danach sicherstellen, dass die Instanz die Pakete kennt docker exec -u 0 "${IOBROKER_CID}" bash -c "iobroker upload javascript" docker exec -u 0 "${IOBROKER_CID}" bash -c "iobroker restart javascript.0 || true"
-
Wie kann ich die erstellten Javascripts, die ich über die GUI aus einem Quellsystem exportiert habe mit der nativen Funktion, per CLI wieder importieren, so dass sie unter "Scripts" in der Admin GUI erscheinen?
Wie kann ich einen Usertree unter 0_userdata wieder per CLI importieren?
-
-
Ich wollte euch mal ein funktionierendes Skript posten, wie ihr voll automatisiert Skripte in IOBroker importieren könnt.
Das Skript geht davon aus, dass ihr eure Skripte vorher per Export aus der Skripte GUI exportiert habt (scripts.zip). Das Zip File hat also eine entsprechend vorgegebene Struktur.
Usage:
sudo ./script.sh <pfad/zur/zip>#!/usr/bin/env bash set -uo pipefail usage() { cat <<'USAGE' Usage: import_iob_scripts_zip_docker.sh <scripts.zip> [--container iobroker] [--instance javascript.0] [--dry-run] Options: --container ioBroker container name (default: iobroker) --instance JavaScript adapter instance (default: javascript.0) --dry-run Print actions only (no changes) USAGE } [[ $# -ge 1 ]] || { usage; exit 1; } ZIP="$1"; shift || true CONTAINER="iobroker" ENGINE_INSTANCE="javascript.0" DRY_RUN=0 while [[ $# -gt 0 ]]; do case "$1" in --container) CONTAINER="${2:-iobroker}"; shift 2 ;; --instance) ENGINE_INSTANCE="${2:-javascript.0}"; shift 2 ;; --dry-run) DRY_RUN=1; shift ;; -h|--help) usage; exit 0 ;; *) echo "Unknown option: $1" >&2; usage; exit 2 ;; esac done for bin in docker unzip jq iconv sed; do command -v "$bin" >/dev/null 2>&1 || { echo "ERROR: missing $bin"; exit 3; } done [[ -f "$ZIP" ]] || { echo "ERROR: ZIP not found: $ZIP"; exit 4; } docker ps --format '{{.Names}}' | grep -qx "$CONTAINER" || { echo "ERROR: container '$CONTAINER' not running"; exit 5; } # Ensure jq in container if ! docker exec -i "$CONTAINER" sh -lc 'command -v jq >/dev/null'; then echo "ERROR: jq fehlt im Container." exit 6 fi iob(){ docker exec -i "$CONTAINER" iobroker "$@"; } TMPDIR="$(mktemp -d -t iob-scripts-zip-XXXXXX)" trap 'rm -rf "$TMPDIR"' EXIT echo "==> Entpacke ZIP auf dem Host: $TMPDIR" unzip -q "$ZIP" -d "$TMPDIR" CONTAINER_TMP="/tmp/iob-scripts-import" docker exec -i "$CONTAINER" sh -lc "rm -rf '$CONTAINER_TMP' && mkdir -p '$CONTAINER_TMP'" >/dev/null echo "==> Stoppe Adapter: $ENGINE_INSTANCE" [[ $DRY_RUN -eq 1 ]] || iob stop "$ENGINE_INSTANCE" # Sammle Ordner (_dir.json) und Skripte (*.json ohne _dir.json) mapfile -d '' DIR_FILES < <(find "$TMPDIR" -type f -name '_dir.json' -print0) mapfile -d '' SCRIPT_FILES < <(find "$TMPDIR" -type f -name '*.json' -print0 | grep -z -v '_dir.json$') TOTAL=$(( ${#DIR_FILES[@]} + ${#SCRIPT_FILES[@]} )) echo "==> Gefundene Objekte: $TOTAL (Ordner: ${#DIR_FILES[@]}, Skripte: ${#SCRIPT_FILES[@]})" [[ $TOTAL -gt 0 ]] || { echo "Keine importierbaren Objekte gefunden."; exit 0; } # Fortschrittsanzeige progress_file="$TMPDIR/progress.count" : > "$progress_file" show_progress() { local total=$1 local width=40 while true; do local done done=$(wc -l < "$progress_file") [[ $done -gt $total ]] && done=$total local filled=$(( done * width / total )) local empty=$(( width - filled )) printf "\r[%s%s] %d/%d" \ "$(printf '#%.0s' $(seq 1 $filled))" \ "$(printf ' %.0s' $(seq 1 $empty))" \ "$done" "$total" [[ $done -ge $total ]] && break sleep 0.2 done echo } show_progress "$TOTAL" & PROGRESS_PID=$! # Import Ordner aus _dir.json (type, common inkl. lokalisierter Namen, native) import_dir() { local dirjson="$1" local obj_id folder_obj obj_id="$(jq -r '._id' "$dirjson")" [[ -n "$obj_id" && "$obj_id" != "null" ]] || return # Übernehme type/common/native vollständig; system Felder (from, ts, acl) lässt ioBroker selbst setzen folder_obj="$(jq -c '{type,common,native}' "$dirjson")" if [[ $DRY_RUN -eq 1 ]]; then echo "[dry-run] Ordner: $obj_id" else docker exec -i "$CONTAINER" iobroker object set "$obj_id" "$folder_obj" fi echo 1 >> "$progress_file" } # Import Skriptobjekt + Source import_script() { local scriptjson="$1" local base rel orig_root root_mapped path obj_id script_key base="$(basename "$scriptjson")" rel="${scriptjson#"$TMPDIR/"}" orig_root="${rel%%/*}" # Mappe lokalisierte Root-Namen case "$orig_root" in "Allgemeine Skripte (common)") root_mapped="common" ;; "Globale Skripte (global)") root_mapped="global" ;; *) root_mapped="$orig_root" ;; esac path="${rel#"$orig_root/"}" path="${path%.json}" path="${path//\//.}" obj_id="script.js.$root_mapped.$path" script_key="$(echo "$obj_id" | tr '/ ' '__')" local common_json_host source_txt_host common_json_host="$TMPDIR/common_${script_key}.json" source_txt_host="$TMPDIR/source_${script_key}.txt" if jq -er 'type=="object" and has("common") and .common|has("source")' "$scriptjson" >/dev/null 2>&1; then local name engineType expert debug verbose enabled name="$(jq -r '.common.name // empty' "$scriptjson")" [[ -n "$name" && "$name" != "null" ]] || name="${base%.json}" engineType="$(jq -r '.common.engineType // "Javascript/Node.js"' "$scriptjson")" expert="$(jq -r '.common.expert // false' "$scriptjson")" debug="$(jq -r '.common.debug // false' "$scriptjson")" verbose="$(jq -r '.common.verbose // false' "$scriptjson")" enabled="$(jq -r '.common.enabled // false' "$scriptjson")" jq -c -n --arg name "$name" \ --arg engine "system.adapter.$ENGINE_INSTANCE" \ --arg engineType "$engineType" \ --argjson expert "$expert" \ --argjson debug "$debug" \ --argjson verbose "$verbose" \ --argjson enabled "$enabled" ' { name:$name, engine:$engine, engineType:$engineType, expert:$expert, debug:$debug, verbose:$verbose, enabled:$enabled }' > "$common_json_host" jq -r '.common.source' "$scriptjson" > "$source_txt_host" else # Reines Source-JSON: Header entfernen, Defaults setzen sed -e '/\/\* -- do not edit following lines - START --/,/-- do not edit previous lines - END --\*\//d' "$scriptjson" > "$source_txt_host" jq -c -n --arg name "${base%.json}" \ --arg engine "system.adapter.$ENGINE_INSTANCE" \ --arg engineType "Javascript/Node.js" ' { name:$name, engine:$engine, engineType:$engineType, expert:false, debug:false, verbose:false, enabled:false }' > "$common_json_host" fi # Encoding säubern iconv -f UTF-8 -t UTF-8 -c "$source_txt_host" | tr -d '\000' > "$source_txt_host.clean" mv "$source_txt_host.clean" "$source_txt_host" if [[ $DRY_RUN -eq 1 ]]; then echo "[dry-run] Script: $obj_id" else # Objekt anlegen (idempotent) docker exec -i "$CONTAINER" iobroker object set "$obj_id" '{"type":"script","common":{"name":""},"native":{}}' >/dev/null # common setzen docker cp "$common_json_host" "$CONTAINER:$CONTAINER_TMP/common_${script_key}.json" >/dev/null docker exec -i "$CONTAINER" sh -lc "C=\$(cat $CONTAINER_TMP/common_${script_key}.json); iobroker object set '$obj_id' \"common=\$C\"" >/dev/null # source setzen (als JSON-String) docker cp "$source_txt_host" "$CONTAINER:$CONTAINER_TMP/source_${script_key}.txt" >/dev/null docker exec -i "$CONTAINER" sh -lc "SRC_JSON=\$(jq -Rn --rawfile s '$CONTAINER_TMP/source_${script_key}.txt' '\$s'); iobroker object set '$obj_id' \"common.source=\$SRC_JSON\"" >/dev/null fi echo 1 >> "$progress_file" } export -f import_dir import_script show_progress export TMPDIR CONTAINER CONTAINER_TMP ENGINE_INSTANCE progress_file echo "==> Importiere Ordner (_dir.json) ..." printf "%s\n" "${DIR_FILES[@]}" \ | xargs -P4 -n1 -I{} bash -c 'import_dir "{}"' echo "==> Importiere Skripte (*.json) ..." printf "%s\n" "${SCRIPT_FILES[@]}" \ | xargs -P4 -n1 -I{} bash -c 'import_script "{}"' # Fortschrittsanzeige beenden wait "$PROGRESS_PID" echo -e "\n==> Import abgeschlossen: $TOTAL Objekte." echo "==> Starte Adapter: $ENGINE_INSTANCE" [[ $DRY_RUN -eq 1 ]] || iob start "$ENGINE_INSTANCE" echo "==> Fertig."
-
Könnt ihr mir sagen wie ich vom mqtt-client username/passwort und host setzen kann?
Alles bis auf Passwort kann ich so setzen:
docker exec -u 0 "${MQTT_CID}" iobroker object extend "system.adapter.${MQTT_ADAPTER}" "$(cat <<EOF { "native": { "type": "client", "host": "mosquitto", "port": 1883, "clientId": "${MQTT_CLIENT_ID}", "username": "${MQTT_USER}", "password": "${MQTT_HASH}", "prefix": "", "publishAll": false, "keepalive": 60, "clean": true, "qos": 0, "retain": false, "subscribe": "#", "ssl": false } } EOF )"
Das Problem ist das Passwort, weil der Adapter einen Hash will, den nur die GUI erzeugt
-
Dann musst du in den Adapter schauen was die gui des Adapters mit dem Passwort macht, was der Nutzer einträgt.