Weiter zum Inhalt
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Hell
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dunkel
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. Blockly + Javascripte aus einer script.json extrahieren

NEWS

  • Der neue Monatsrückblick für Mai und Juni 2026 ist online!
    BluefoxB
    Bluefox
    8
    1
    224

  • wichtiges UPDATE für controller 7.2.2 im stable
    HomoranH
    Homoran
    10
    1
    3.3k

  • Neues YouTube-Video: Visualisierung im Devices-Adapter
    BluefoxB
    Bluefox
    16
    1
    5.1k

Blockly + Javascripte aus einer script.json extrahieren

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
11 Beiträge 5 Kommentatoren 785 Aufrufe 6 Beobachtet
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • Michael SchmittM Online
    Michael SchmittM Online
    Michael Schmitt
    schrieb am zuletzt editiert von Michael Schmitt
    #1

    Hallo,
    da ich im Moment meine Scripte aufräume und auch ein paar geändert habe, ist mir im Eifer des Gefechts leider ein Fehler passiert und ich habe ein Script (Blockly)was ich noch brauchte überschrieben und gespeichert.
    Ist ja kein Problem ich mach ja mit Backitup immer schön Backups, auch von Javascript (dachte ich). Leider mußte ich feststellen das in der Backupdatei nur eine große Datei ist und zwar die script.json. Mit der konnte ich erstmal garnichts anfangen.
    Na dann frag ich mal ChatGPT und mit desen Hilfe haben wir folgendes gebaut.

    Das muss man tun. Ich werde hier die Pfade,Ordner und Dateinamen nennen so wie ich sie nutze. Sollte ihr andere nutzen dann müßt ihr das in den Dateien

    1-Export-ioBroker.ps1
    2-Extract-Blockly.ps1

    ändern. Steht jeweils ganz oben darin.

    Auf Laufwerk C habe ich einen Ordner erstellt = iobroker_Script_recover
    Dann geht ihr in diesen Ordner und ertellt eine Textdatei und da fügt ihr folgendes ein.

    $jsonFile = "C:\iobroker_Script_recover\script.json"
    $root = "C:\iobroker_Script_recover\Export"
    
    New-Item -ItemType Directory -Force -Path $root | Out-Null
    
    $report = @()
    
    [int]$countJS = 0
    [int]$countBlockly = 0
    [int]$countDisabled = 0
    
    $json = Get-Content $jsonFile -Raw | ConvertFrom-Json
    
    foreach ($property in $json.PSObject.Properties) {
    
        $id = $property.Name
        $obj = $property.Value
    
        if (-not $obj.common) { continue }
        if (-not $obj.common.source) { continue }
    
        $enabled = $obj.common.enabled
        $engineType = $obj.common.engineType
    
        $relative = $id -replace '^script\.js\.common\.', ''
        $parts = $relative.Split('.')
    
        $scriptName = $parts[-1]
    
        if ($parts.Count -gt 1) {
            $folder = Join-Path $root ($parts[0..($parts.Count-2)] -join '\')
        }
        else {
            $folder = $root
        }
    
        if (-not $enabled) {
            $folder = Join-Path $folder "_DEAKTIVIERT"
            $countDisabled++
        }
    
        New-Item -ItemType Directory -Force -Path $folder | Out-Null
    
        $source = $obj.common.source
    
        $xmlMatch = [regex]::Match(
            $source,
            '<xml[\s\S]*?</xml>'
        )
    
        if ($xmlMatch.Success) {
    
            $xmlFile = Join-Path $folder ($scriptName + ".xml")
    
            $xmlMatch.Value |
                Set-Content $xmlFile -Encoding UTF8
    
            $source =
                $source.Replace(
                    $xmlMatch.Value,
                    ""
                ).Trim()
    
            $countBlockly++
        }
        else {
            $countJS++
        }
    
        $jsFile = Join-Path $folder ($scriptName + ".js")
    
        $source |
            Set-Content $jsFile -Encoding UTF8
    
        $report += [PSCustomObject]@{
            Name       = $scriptName
            Aktiv      = $enabled
            EngineType = $engineType
            Datei      = $jsFile
        }
    
        Write-Host "Exportiert: $scriptName"
    }
    
    $csvFile = Join-Path $root "Scriptliste.csv"
    
    $report |
        Export-Csv `
            -Path $csvFile `
            -NoTypeInformation `
            -Encoding UTF8
    
    $statFile = Join-Path $root "Statistik.txt"
    
    @"
    Gesamt exportiert : $($report.Count)
    JavaScript        : $countJS
    Blockly           : $countBlockly
    Deaktiviert       : $countDisabled
    "@ | Set-Content $statFile -Encoding UTF8
    
    Write-Host ""
    Write-Host "Export abgeschlossen."
    Write-Host "Ordner: $root"
    

    Das nennt ihr = 1-Export-ioBroker.ps1
    Das ganze dann so speichern unter, in den selben Ordner.
    einstellungen.jpg

    Jetzt die zweite Datei. Wieder ein Textdokument erstellen und dort das einfügen.

    Add-Type -AssemblyName System.Web
    
    $root = "C:\iobroker_Script_recover\Export"
    
    $count = 0
    
    Get-ChildItem $root -Recurse -Filter *.js | ForEach-Object {
    
        $file = $_.FullName
        $content = Get-Content $file -Raw
    
        if ($content -match '//([A-Za-z0-9+/=%]+)\s*$') {
    
            try {
    
                $encoded = $Matches[1]
    
                $bytes = [Convert]::FromBase64String($encoded)
    
                $urlText = [Text.Encoding]::UTF8.GetString($bytes)
    
                $xml = [System.Web.HttpUtility]::UrlDecode($urlText)
    
                if ($xml.StartsWith("<xml")) {
    
                    $xmlFile =
                        [System.IO.Path]::ChangeExtension(
                            $file,
                            ".xml"
                        )
    
                    $xml |
                        Set-Content $xmlFile -Encoding UTF8
    
                    Write-Host "Blockly:" $xmlFile
    
                    $count++
                }
            }
            catch {
            }
        }
    }
    
    Write-Host ""
    Write-Host "Blockly-Dateien gefunden:" $count
    

    Das nennt ihr = 2-Extract-Blockly.ps1
    Das ganze dann so speichern unter, in den selben Ordner.
    einstellungen2.jpg

    Zum testen könnt ihr nun mal aus einer Javascript Backupdatei ( sieht ungefair so aus javascripts_2026_06_29-03_37_23_backupiobroker.tar.gz) die script.json entpacken.

    Die script.json kommt jetzt auch in den auf C erstellten Ordner "iobroker_Script_recover"

    Nun Powershell öffnen und dort die Befehle nacheinander eingeben und mit Enter bestättigen.

    cd C:\iobroker_Script_recover
    .\1-Export-ioBroker.ps1
    .\2-Extract-Blockly.ps1

    Jetzt sollte in dem Ordner ein weiterer Odner "Export" erstellt worden sein.
    1.jpg

    In Export
    2.jpg

    auch xml für Blocklys
    xml.jpg

    In diesem Ordner sind alle Javascripte + Blocklys in der korrekte Ordnerstruktur vorhanden. Sollten Scripte deaktiviert sein, sind diese in einem extra Ordner Names "DEAKTIVIERT"

    Bei mir hat das funtioniert und ich konnte mein Blockly einfach wieder exportieren und es läuft.

    ########################################
    EDIT: 21.07.2026 - Hier die EXE direkt für Windows
    ioBroker_Script_Extractor.exe (gepackte Version) = ioBroker_Script_Extractor.zip
    ########################################

    Gruß Michael

    1 Antwort Letzte Antwort
    1
    • Michael SchmittM Online
      Michael SchmittM Online
      Michael Schmitt
      schrieb am zuletzt editiert von Michael Schmitt
      #2

      Hallo,

      hab jetzt noch ein wenig daran gebaut (Ki war der Macher) und nun schaut es so aus.
      Ist nur mit Javascript und Blockly getestet.

      Jetzt mit

      • Suchfunktion
      • einzelne Scripte wiederherstellen
      • direkt in Zwischenablage kopieren
      • deaktivierte Scripte ausblenden
      • Namens oder Ordnersuche

      1.jpg

      • Ordner in C:\iobroker_Script_recover erstellen
        in diesen Ordner
      • Datei erstellen: ioBroker_Script_Extractor.ps1 mit diesem Inhalt.
      Add-Type -AssemblyName System.Windows.Forms
      Add-Type -AssemblyName System.Drawing
      Add-Type -AssemblyName System.Web
      
      $fd = New-Object System.Windows.Forms.OpenFileDialog
      $fd.InitialDirectory = "C:\"
      $fd.Filter = "script.json|*.json"
      $fd.Title = "Wähle script.json"
      
      if ($fd.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { exit }
      
      $raw = Get-Content $fd.FileName -Raw
      if ([string]::IsNullOrWhiteSpace($raw)) { exit }
      $json = ConvertFrom-Json $raw
      $skriptListe = [System.Collections.Generic.List[Object]]::new()
      
      function Get-SichererBlocklyXml ([string]$sourceCode) {
          if ([string]::IsNullOrWhiteSpace($sourceCode)) { return "" }
          if ($sourceCode -match '//([A-Za-z0-9+/=%]{50,})\s*$') {
              try {
                  $xml = [System.Web.HttpUtility]::UrlDecode([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Matches[1])))
                  if ($xml -match '<xml') { return ($xml -replace '><', ">`r`n<").Trim() }
              } catch {}
          }
          if ($sourceCode -match '(?s)(<xml.*?</xml>)') { return ($Matches[1] -replace '><', ">`r`n<").Trim() }
          return ""
      }
      
      function Add-SkriptToList ([string]$id, $obj) {
          if (-not $obj -or ($obj.type -match "channel|folder")) { return }
          $saubereID = $id -replace '^script\.js\.(common\.|global\.)?', ''
          $parts = $saubereID.Split('.')
          $p = "Hauptordner"
          if ($parts.Count -gt 1) { $p = $parts[0..($parts.Count-2)] -join '/' }
          $src = ""
          if ($obj.common -and $obj.common.source) { $src = $obj.common.source }
          elseif ($obj.source) { $src = $obj.source }
          $en = $true
          if ($obj.common -and $obj.common.enabled -ne $null) { $en = $obj.common.enabled }
          $xml = Get-SichererBlocklyXml -sourceCode $src
          $skriptListe.Add([PSCustomObject]@{
              Name = $parts[-1]; Pfad = $p; Internal = $id; JsSource = $src; XmlSource = $xml;
              Typ = if ($xml) { "Blockly" } else { "JavaScript" }; Status = if ($en) { "Aktiv" } else { "Deaktiviert" }
          })
      }
      
      if ($json.PSObject.Properties.Name -contains "objects") {
          foreach ($p in $json.objects.PSObject.Properties) { if ($p.Name -like "script.js.*") { Add-SkriptToList -id $p.Name -obj $p.Value } }
      } else {
          foreach ($p in $json.PSObject.Properties) { if ($p.Name -like "script.js.*") { Add-SkriptToList -id $p.Name -obj $p.Value } }
      }
      if ($skriptListe.Count -eq 0) { exit }
      
      $f = New-Object System.Windows.Forms.Form
      $f.Text = "ioBroker Script Extractor"
      $f.Size = New-Object System.Drawing.Size(850, 750)
      $f.StartPosition = "CenterScreen"
      
      $txtSearch = New-Object System.Windows.Forms.TextBox
      $txtSearch.Location = New-Object System.Drawing.Point(65, 12)
      $txtSearch.Size = New-Object System.Drawing.Size(450, 20)
      $f.Controls.Add($txtSearch)
      
      $chkHide = New-Object System.Windows.Forms.CheckBox
      $chkHide.Text = "Deaktivierte ausblenden"
      $chkHide.Location = New-Object System.Drawing.Point(530, 10)
      $chkHide.Size = New-Object System.Drawing.Size(180, 24)
      $f.Controls.Add($chkHide)
      
      $lblCount = New-Object System.Windows.Forms.Label
      $lblCount.Text = "Gefundene Skripte: 0"
      $lblCount.Location = New-Object System.Drawing.Point(10, 45)
      $lblCount.Size = New-Object System.Drawing.Size(400, 20)
      $lblCount.Font = New-Object System.Drawing.Font("Arial", 9, [System.Drawing.FontStyle]::Bold)
      $f.Controls.Add($lblCount)
      
      $dgv = New-Object System.Windows.Forms.DataGridView
      $dgv.Location = New-Object System.Drawing.Point(10, 70)
      $dgv.Size = New-Object System.Drawing.Size(810, 240)
      $dgv.AutoSizeColumnsMode = "Fill"
      $dgv.SelectionMode = "FullRowSelect"
      $dgv.MultiSelect = $true
      $dgv.AllowUserToAddRows = $false
      $dgv.ReadOnly = $true
      $dgv.RowHeadersVisible = $false
      $f.Controls.Add($dgv)
      
      $lblPrev = New-Object System.Windows.Forms.Label
      $lblPrev.Text = "Vorschau:"
      $lblPrev.Location = New-Object System.Drawing.Point(10, 325)
      $lblPrev.Size = New-Object System.Drawing.Size(500, 20)
      $f.Controls.Add($lblPrev)
      
      $txtPrev = New-Object System.Windows.Forms.RichTextBox
      $txtPrev.Location = New-Object System.Drawing.Point(10, 345)
      $txtPrev.Size = New-Object System.Drawing.Size(810, 260)
      $txtPrev.Font = New-Object System.Drawing.Font("Consolas", 10)
      $txtPrev.ReadOnly = $true
      $txtPrev.WordWrap = $false
      $f.Controls.Add($txtPrev)
      
      $btnClip = New-Object System.Windows.Forms.Button
      $btnClip.Text = "📋 In Zwischenablage"
      $btnClip.Location = New-Object System.Drawing.Point(10, 620)
      $btnClip.Size = New-Object System.Drawing.Size(160, 35)
      $f.Controls.Add($btnClip)
      
      $btnAll = New-Object System.Windows.Forms.Button
      $btnAll.Text = "📦 Alle exportieren"
      $btnAll.Location = New-Object System.Drawing.Point(180, 620)
      $btnAll.Size = New-Object System.Drawing.Size(150, 35)
      $btnAll.DialogResult = 7
      $f.Controls.Add($btnAll)
      
      $btnOk = New-Object System.Windows.Forms.Button
      $btnOk.Text = "Ausgewählte exportieren"
      $btnOk.Location = New-Object System.Drawing.Point(500, 620)
      $btnOk.Size = New-Object System.Drawing.Size(160, 35)
      $btnOk.DialogResult = [System.Windows.Forms.DialogResult]::OK
      $f.Controls.Add($btnOk)
      
      $btnCan = New-Object System.Windows.Forms.Button
      $btnCan.Text = "Abbrechen"
      $btnCan.Location = New-Object System.Drawing.Point(670, 620)
      $btnCan.Size = New-Object System.Drawing.Size(150, 35)
      $btnCan.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
      $f.Controls.Add($btnCan)
      
      $lblSrc = New-Object System.Windows.Forms.Label -Property @{Text="Suchen:"; Location=New-Object System.Drawing.Point(10,15); Size=New-Object System.Drawing.Size(50,20)}
      $f.Controls.Add($lblSrc)
      
      # KORREKTUR: Wildcards (*...) wieder korrekt für den Pfad-Vergleich aktiviert
      function Update-Grid {
          $filtered = $skriptListe | Where-Object { [string]::IsNullOrWhiteSpace($txtSearch.Text) -or $_.Name -like "*$($txtSearch.Text)*" -or $_.Pfad -like "*$($txtSearch.Text)*" } | Where-Object { -not $chkHide.Checked -or $_.Status -eq "Aktiv" }
          $dt = New-Object System.Data.DataTable; [void]$dt.Columns.AddRange(@("Name","ioBroker-Pfad","Typ","Status","InternalID"))
          foreach ($item in $filtered) { [void]$dt.Rows.Add($item.Name, $item.Pfad, $item.Typ, $item.Status, $item.Internal) }
          $dgv.DataSource = $dt; if ($dgv.Columns["InternalID"]) { $dgv.Columns["InternalID"].Visible = $false }
          $lblCount.Text = "Gefundene Skripte (aktueller Filter): " + $filtered.Count
          $lblPrev.Text = "Vorschau (0 Skripte ausgewählt):"
      }
      $txtSearch.Add_TextChanged({ Update-Grid }); $chkHide.Add_CheckedChanged({ Update-Grid })
      
      $dgv.Add_SelectionChanged({
          $anz = $dgv.SelectedRows.Count
          if ($anz -gt 0) {
              $selID = [string]$dgv.SelectedRows[0].Cells["InternalID"].Value
              if (-not [string]::IsNullOrEmpty($selID)) {
                  $m = $skriptListe | Where-Object { $_.Internal -eq $selID }
                  if ($m) { $txtPrev.Text = if ($m.Typ -eq "Blockly" -and $m.XmlSource) { $m.XmlSource } else { $m.JsSource } }
              }
              $lblPrev.Text = if ($anz -eq 1) { "Code-Vorschau (1 Skript ausgewählt):" } else { "Code-Vorschau ($anz Skripte ausgewählt):" }
          } else { $txtPrev.Text = ""; $lblPrev.Text = "Vorschau (0 Skripte ausgewählt):" }
      })
      
      $btnClip.Add_Click({
          if (-not [string]::IsNullOrEmpty($txtPrev.Text)) {
              [System.Windows.Forms.Clipboard]::SetText($txtPrev.Text)
              $global:oldText = $btnClip.Text; $btnClip.Text = "✅ Kopiert!"; $btnClip.Enabled = $false
              $global:clipTimer = New-Object System.Windows.Forms.Timer -Property @{ Interval = 1500 }
              $global:clipTimer.Add_Tick({ $btnClip.Text = $global:oldText; $btnClip.Enabled = $true; $global:clipTimer.Stop(); $global:clipTimer.Dispose() }); $global:clipTimer.Start()
          }
      })
      
      Update-Grid
      $res = $f.ShowDialog(); $ausIDs = @()
      if ($res -eq 7) { foreach ($r in $dgv.Rows) { $ausIDs += [string]$r.Cells["InternalID"].Value } }
      elseif ($res -eq [System.Windows.Forms.DialogResult]::OK -and $dgv.SelectedRows.Count -gt 0) { foreach ($r in $dgv.SelectedRows) { $ausIDs += [string]$r.Cells["InternalID"].Value } }
      else { $f.Dispose(); exit }; $f.Dispose()
      
      $folderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
      if ($folderDialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { exit }
      $root = Join-Path $folderDialog.SelectedPath ("ioBroker_Backup_" + (Get-Date -Format "yyyy-MM-dd_HH-mm"))
      New-Item -ItemType Directory -Force -Path $root | Out-Null
      
      foreach ($targetID in $ausIDs) {
          $scriptData = $skriptListe | Where-Object { $_.Internal -eq $targetID }
          if (-not $scriptData) { continue }
          $folder = if ($scriptData.Pfad -ne "Hauptordner") { Join-Path $root ($scriptData.Pfad -replace '/', '\') } else { $root }
          if ($scriptData.Status -eq "Deaktiviert") { $folder = Join-Path $folder "_DEAKTIVIERT" }
          New-Item -ItemType Directory -Force -Path $folder | Out-Null
          if ($scriptData.Typ -eq "Blockly" -and $scriptData.XmlSource) {
              $scriptData.XmlSource | Set-Content (Join-Path $folder ($scriptData.Name + ".xml")) -Encoding UTF8
              $cleanJs = $scriptData.JsSource
              if ($cleanJs -match '//([A-Za-z0-9+/=%]{50,})\s*$') { $cleanJs = $cleanJs.Replace($Matches[0], "").Trim() }
              if ($cleanJs -match '(?s)<xml.*?></xml>') { $cleanJs = $cleanJs.Replace($Matches[0], "").Trim() }
              $cleanJs | Set-Content (Join-Path $folder ($scriptData.Name + ".js")) -Encoding UTF8
          } else { $scriptData.JsSource | Set-Content (Join-Path $folder ($scriptData.Name + ".js")) -Encoding UTF8 }
      }
      Invoke-Item $root
      
      
      • Powershell öffnen
      • cd C:\iobroker_Script_recover
      • .\ioBroker_Script_Extractor.ps1
      • script.json aus dem Javascript Backup auswählen (muß man davor natürlich entpacken)

      3.jpg

      hier zB mit deaktivierten scripten ausblenden
      2.jpg

      ich hoffe es hilft jemandem ;)

      ########################################
      EDIT: 21.07.2026 - Hier die EXE direkt für Windows
      ioBroker_Script_Extractor.exe (gepackte Version) = ioBroker_Script_Extractor.zip
      ########################################

      F 1 Antwort Letzte Antwort
      0
      • Michael SchmittM Michael Schmitt

        Hallo,

        hab jetzt noch ein wenig daran gebaut (Ki war der Macher) und nun schaut es so aus.
        Ist nur mit Javascript und Blockly getestet.

        Jetzt mit

        • Suchfunktion
        • einzelne Scripte wiederherstellen
        • direkt in Zwischenablage kopieren
        • deaktivierte Scripte ausblenden
        • Namens oder Ordnersuche

        1.jpg

        • Ordner in C:\iobroker_Script_recover erstellen
          in diesen Ordner
        • Datei erstellen: ioBroker_Script_Extractor.ps1 mit diesem Inhalt.
        Add-Type -AssemblyName System.Windows.Forms
        Add-Type -AssemblyName System.Drawing
        Add-Type -AssemblyName System.Web
        
        $fd = New-Object System.Windows.Forms.OpenFileDialog
        $fd.InitialDirectory = "C:\"
        $fd.Filter = "script.json|*.json"
        $fd.Title = "Wähle script.json"
        
        if ($fd.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { exit }
        
        $raw = Get-Content $fd.FileName -Raw
        if ([string]::IsNullOrWhiteSpace($raw)) { exit }
        $json = ConvertFrom-Json $raw
        $skriptListe = [System.Collections.Generic.List[Object]]::new()
        
        function Get-SichererBlocklyXml ([string]$sourceCode) {
            if ([string]::IsNullOrWhiteSpace($sourceCode)) { return "" }
            if ($sourceCode -match '//([A-Za-z0-9+/=%]{50,})\s*$') {
                try {
                    $xml = [System.Web.HttpUtility]::UrlDecode([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Matches[1])))
                    if ($xml -match '<xml') { return ($xml -replace '><', ">`r`n<").Trim() }
                } catch {}
            }
            if ($sourceCode -match '(?s)(<xml.*?</xml>)') { return ($Matches[1] -replace '><', ">`r`n<").Trim() }
            return ""
        }
        
        function Add-SkriptToList ([string]$id, $obj) {
            if (-not $obj -or ($obj.type -match "channel|folder")) { return }
            $saubereID = $id -replace '^script\.js\.(common\.|global\.)?', ''
            $parts = $saubereID.Split('.')
            $p = "Hauptordner"
            if ($parts.Count -gt 1) { $p = $parts[0..($parts.Count-2)] -join '/' }
            $src = ""
            if ($obj.common -and $obj.common.source) { $src = $obj.common.source }
            elseif ($obj.source) { $src = $obj.source }
            $en = $true
            if ($obj.common -and $obj.common.enabled -ne $null) { $en = $obj.common.enabled }
            $xml = Get-SichererBlocklyXml -sourceCode $src
            $skriptListe.Add([PSCustomObject]@{
                Name = $parts[-1]; Pfad = $p; Internal = $id; JsSource = $src; XmlSource = $xml;
                Typ = if ($xml) { "Blockly" } else { "JavaScript" }; Status = if ($en) { "Aktiv" } else { "Deaktiviert" }
            })
        }
        
        if ($json.PSObject.Properties.Name -contains "objects") {
            foreach ($p in $json.objects.PSObject.Properties) { if ($p.Name -like "script.js.*") { Add-SkriptToList -id $p.Name -obj $p.Value } }
        } else {
            foreach ($p in $json.PSObject.Properties) { if ($p.Name -like "script.js.*") { Add-SkriptToList -id $p.Name -obj $p.Value } }
        }
        if ($skriptListe.Count -eq 0) { exit }
        
        $f = New-Object System.Windows.Forms.Form
        $f.Text = "ioBroker Script Extractor"
        $f.Size = New-Object System.Drawing.Size(850, 750)
        $f.StartPosition = "CenterScreen"
        
        $txtSearch = New-Object System.Windows.Forms.TextBox
        $txtSearch.Location = New-Object System.Drawing.Point(65, 12)
        $txtSearch.Size = New-Object System.Drawing.Size(450, 20)
        $f.Controls.Add($txtSearch)
        
        $chkHide = New-Object System.Windows.Forms.CheckBox
        $chkHide.Text = "Deaktivierte ausblenden"
        $chkHide.Location = New-Object System.Drawing.Point(530, 10)
        $chkHide.Size = New-Object System.Drawing.Size(180, 24)
        $f.Controls.Add($chkHide)
        
        $lblCount = New-Object System.Windows.Forms.Label
        $lblCount.Text = "Gefundene Skripte: 0"
        $lblCount.Location = New-Object System.Drawing.Point(10, 45)
        $lblCount.Size = New-Object System.Drawing.Size(400, 20)
        $lblCount.Font = New-Object System.Drawing.Font("Arial", 9, [System.Drawing.FontStyle]::Bold)
        $f.Controls.Add($lblCount)
        
        $dgv = New-Object System.Windows.Forms.DataGridView
        $dgv.Location = New-Object System.Drawing.Point(10, 70)
        $dgv.Size = New-Object System.Drawing.Size(810, 240)
        $dgv.AutoSizeColumnsMode = "Fill"
        $dgv.SelectionMode = "FullRowSelect"
        $dgv.MultiSelect = $true
        $dgv.AllowUserToAddRows = $false
        $dgv.ReadOnly = $true
        $dgv.RowHeadersVisible = $false
        $f.Controls.Add($dgv)
        
        $lblPrev = New-Object System.Windows.Forms.Label
        $lblPrev.Text = "Vorschau:"
        $lblPrev.Location = New-Object System.Drawing.Point(10, 325)
        $lblPrev.Size = New-Object System.Drawing.Size(500, 20)
        $f.Controls.Add($lblPrev)
        
        $txtPrev = New-Object System.Windows.Forms.RichTextBox
        $txtPrev.Location = New-Object System.Drawing.Point(10, 345)
        $txtPrev.Size = New-Object System.Drawing.Size(810, 260)
        $txtPrev.Font = New-Object System.Drawing.Font("Consolas", 10)
        $txtPrev.ReadOnly = $true
        $txtPrev.WordWrap = $false
        $f.Controls.Add($txtPrev)
        
        $btnClip = New-Object System.Windows.Forms.Button
        $btnClip.Text = "📋 In Zwischenablage"
        $btnClip.Location = New-Object System.Drawing.Point(10, 620)
        $btnClip.Size = New-Object System.Drawing.Size(160, 35)
        $f.Controls.Add($btnClip)
        
        $btnAll = New-Object System.Windows.Forms.Button
        $btnAll.Text = "📦 Alle exportieren"
        $btnAll.Location = New-Object System.Drawing.Point(180, 620)
        $btnAll.Size = New-Object System.Drawing.Size(150, 35)
        $btnAll.DialogResult = 7
        $f.Controls.Add($btnAll)
        
        $btnOk = New-Object System.Windows.Forms.Button
        $btnOk.Text = "Ausgewählte exportieren"
        $btnOk.Location = New-Object System.Drawing.Point(500, 620)
        $btnOk.Size = New-Object System.Drawing.Size(160, 35)
        $btnOk.DialogResult = [System.Windows.Forms.DialogResult]::OK
        $f.Controls.Add($btnOk)
        
        $btnCan = New-Object System.Windows.Forms.Button
        $btnCan.Text = "Abbrechen"
        $btnCan.Location = New-Object System.Drawing.Point(670, 620)
        $btnCan.Size = New-Object System.Drawing.Size(150, 35)
        $btnCan.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
        $f.Controls.Add($btnCan)
        
        $lblSrc = New-Object System.Windows.Forms.Label -Property @{Text="Suchen:"; Location=New-Object System.Drawing.Point(10,15); Size=New-Object System.Drawing.Size(50,20)}
        $f.Controls.Add($lblSrc)
        
        # KORREKTUR: Wildcards (*...) wieder korrekt für den Pfad-Vergleich aktiviert
        function Update-Grid {
            $filtered = $skriptListe | Where-Object { [string]::IsNullOrWhiteSpace($txtSearch.Text) -or $_.Name -like "*$($txtSearch.Text)*" -or $_.Pfad -like "*$($txtSearch.Text)*" } | Where-Object { -not $chkHide.Checked -or $_.Status -eq "Aktiv" }
            $dt = New-Object System.Data.DataTable; [void]$dt.Columns.AddRange(@("Name","ioBroker-Pfad","Typ","Status","InternalID"))
            foreach ($item in $filtered) { [void]$dt.Rows.Add($item.Name, $item.Pfad, $item.Typ, $item.Status, $item.Internal) }
            $dgv.DataSource = $dt; if ($dgv.Columns["InternalID"]) { $dgv.Columns["InternalID"].Visible = $false }
            $lblCount.Text = "Gefundene Skripte (aktueller Filter): " + $filtered.Count
            $lblPrev.Text = "Vorschau (0 Skripte ausgewählt):"
        }
        $txtSearch.Add_TextChanged({ Update-Grid }); $chkHide.Add_CheckedChanged({ Update-Grid })
        
        $dgv.Add_SelectionChanged({
            $anz = $dgv.SelectedRows.Count
            if ($anz -gt 0) {
                $selID = [string]$dgv.SelectedRows[0].Cells["InternalID"].Value
                if (-not [string]::IsNullOrEmpty($selID)) {
                    $m = $skriptListe | Where-Object { $_.Internal -eq $selID }
                    if ($m) { $txtPrev.Text = if ($m.Typ -eq "Blockly" -and $m.XmlSource) { $m.XmlSource } else { $m.JsSource } }
                }
                $lblPrev.Text = if ($anz -eq 1) { "Code-Vorschau (1 Skript ausgewählt):" } else { "Code-Vorschau ($anz Skripte ausgewählt):" }
            } else { $txtPrev.Text = ""; $lblPrev.Text = "Vorschau (0 Skripte ausgewählt):" }
        })
        
        $btnClip.Add_Click({
            if (-not [string]::IsNullOrEmpty($txtPrev.Text)) {
                [System.Windows.Forms.Clipboard]::SetText($txtPrev.Text)
                $global:oldText = $btnClip.Text; $btnClip.Text = "✅ Kopiert!"; $btnClip.Enabled = $false
                $global:clipTimer = New-Object System.Windows.Forms.Timer -Property @{ Interval = 1500 }
                $global:clipTimer.Add_Tick({ $btnClip.Text = $global:oldText; $btnClip.Enabled = $true; $global:clipTimer.Stop(); $global:clipTimer.Dispose() }); $global:clipTimer.Start()
            }
        })
        
        Update-Grid
        $res = $f.ShowDialog(); $ausIDs = @()
        if ($res -eq 7) { foreach ($r in $dgv.Rows) { $ausIDs += [string]$r.Cells["InternalID"].Value } }
        elseif ($res -eq [System.Windows.Forms.DialogResult]::OK -and $dgv.SelectedRows.Count -gt 0) { foreach ($r in $dgv.SelectedRows) { $ausIDs += [string]$r.Cells["InternalID"].Value } }
        else { $f.Dispose(); exit }; $f.Dispose()
        
        $folderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
        if ($folderDialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { exit }
        $root = Join-Path $folderDialog.SelectedPath ("ioBroker_Backup_" + (Get-Date -Format "yyyy-MM-dd_HH-mm"))
        New-Item -ItemType Directory -Force -Path $root | Out-Null
        
        foreach ($targetID in $ausIDs) {
            $scriptData = $skriptListe | Where-Object { $_.Internal -eq $targetID }
            if (-not $scriptData) { continue }
            $folder = if ($scriptData.Pfad -ne "Hauptordner") { Join-Path $root ($scriptData.Pfad -replace '/', '\') } else { $root }
            if ($scriptData.Status -eq "Deaktiviert") { $folder = Join-Path $folder "_DEAKTIVIERT" }
            New-Item -ItemType Directory -Force -Path $folder | Out-Null
            if ($scriptData.Typ -eq "Blockly" -and $scriptData.XmlSource) {
                $scriptData.XmlSource | Set-Content (Join-Path $folder ($scriptData.Name + ".xml")) -Encoding UTF8
                $cleanJs = $scriptData.JsSource
                if ($cleanJs -match '//([A-Za-z0-9+/=%]{50,})\s*$') { $cleanJs = $cleanJs.Replace($Matches[0], "").Trim() }
                if ($cleanJs -match '(?s)<xml.*?></xml>') { $cleanJs = $cleanJs.Replace($Matches[0], "").Trim() }
                $cleanJs | Set-Content (Join-Path $folder ($scriptData.Name + ".js")) -Encoding UTF8
            } else { $scriptData.JsSource | Set-Content (Join-Path $folder ($scriptData.Name + ".js")) -Encoding UTF8 }
        }
        Invoke-Item $root
        
        
        • Powershell öffnen
        • cd C:\iobroker_Script_recover
        • .\ioBroker_Script_Extractor.ps1
        • script.json aus dem Javascript Backup auswählen (muß man davor natürlich entpacken)

        3.jpg

        hier zB mit deaktivierten scripten ausblenden
        2.jpg

        ich hoffe es hilft jemandem ;)

        ########################################
        EDIT: 21.07.2026 - Hier die EXE direkt für Windows
        ioBroker_Script_Extractor.exe (gepackte Version) = ioBroker_Script_Extractor.zip
        ########################################

        F Online
        F Online
        fastfoot
        schrieb am zuletzt editiert von
        #3

        @Michael-Schmitt sagte:

        hab jetzt noch ein wenig daran gebaut

        Eigentlich eine gute Idee und auch schön umgesetzt! Aber schau dir doch mal den relativ neuen Adapter script-restore von @david-g. an. Der hat eine GUI und erspart einem jegliches Gefummel und entpacken. Z.Zt. nur im Beta, kommt aber schon bald ins Stable

        iobroker läuft unter Docker auf Windows (WSL2)

        1 Antwort Letzte Antwort
        0
        • Michael SchmittM Online
          Michael SchmittM Online
          Michael Schmitt
          schrieb am zuletzt editiert von
          #4

          Hi,
          ja hab ich schon gesehen/gelesen. Super gemacht und eine super Idee. Ich für mich möchte aber deswegen keinen Adapter installieren und mach sowas außerhalb. So oft wird man das ja eh nicht brauchen (hoffentlich)

          Gruß

          1 Antwort Letzte Antwort
          1
          • Michael SchmittM Online
            Michael SchmittM Online
            Michael Schmitt
            schrieb am zuletzt editiert von Michael Schmitt
            #5

            Hi,
            hab noch ein wenig mit rum gespielt. Nun kann direkt die zB javascripts_2026_07_20-03_37_23_backupiobroker.tar.gz ohne entpacken oder eine entpackte script.json genutzt werden.

            Zudem kann die Suche verfeinert werden=
            Alle Typen - Nur Blockly - Nur Javascript
            111.jpg

            • Ordner in C:\iobroker_Script_recover erstellen
              in diesen Ordner
            • Datei erstellen: ioBroker_Script_Extractor.ps1 mit diesem Inhalt.
            Add-Type -AssemblyName System.Windows.Forms
            Add-Type -AssemblyName System.Drawing
            Add-Type -AssemblyName System.Web
            Add-Type -AssemblyName System.IO.Compression
            Add-Type -AssemblyName System.IO.Compression.FileSystem
            
            $so = "C:\iobroker_Script_recover"
            $jp = ""
            
            # 1. Komfortabler Auswahldialog für den Benutzer
            $fdSelect = New-Object System.Windows.Forms.OpenFileDialog
            $fdSelect.InitialDirectory = $so
            $fdSelect.Filter = "ioBroker Backups (*.json;*.tar.gz)|*.json;*.tar.gz|Alle Dateien (*.*)|*.*"
            $fdSelect.Title = "Bitte wähle deine script.json ODER eine ioBroker .tar.gz Backup-Datei aus"
            
            if ($fdSelect.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { exit }
            $gewaehlteDatei = $fdSelect.FileName
            
            # Erkennung der .tar.gz Endung und Entpacken im Arbeitsspeicher
            if ($gewaehlteDatei.ToLower().EndsWith(".tar.gz")) {
                Write-Host "Backup-Archiv ausgewählt: $(Split-Path $gewaehlteDatei -Leaf)" -ForegroundColor Yellow
                Write-Host "Entpacke script.json mit Windows-Systemkern..." -ForegroundColor Cyan
                
                $tmp = Join-Path $so "temp_extract"
                if (Test-Path $tmp) { Remove-Item $tmp -Recurse -Force -EA SilentlyContinue }
                New-Item -ItemType Directory -Force -Path $tmp | Out-Null
                
                try {
                    $inFile = New-Object System.IO.FileInfo($gewaehlteDatei)
                    $gzStream = New-Object System.IO.Compression.GZipStream($inFile.OpenRead(), [System.IO.Compression.CompressionMode]::Decompress)
                    
                    $tarPath = Join-Path $tmp "temp_backup.tar"
                    $tarOutStream = [System.IO.File]::Create($tarPath)
                    $gzStream.CopyTo($tarOutStream)
                    $tarOutStream.Close(); $gzStream.Close()
                    
                    $tarFileStream = [System.IO.File]::OpenRead($tarPath)
                    $buffer = New-Object byte[] 512
                    
                    while ($tarFileStream.Read($buffer, 0, 512) -eq 512) {
                        $name = [System.Text.Encoding]::ASCII.GetString($buffer, 0, 100).Trim(([char]0))
                        $sizeString = [System.Text.Encoding]::ASCII.GetString($buffer, 124, 12).Trim()
                        $size = if ($sizeString) { [Convert]::ToInt64($sizeString, 8) } else { 0 }
                        
                        if ($name.ToLower().EndsWith("script.json")) {
                            $outJp = Join-Path $tmp "script.json"
                            $outStream = [System.IO.File]::Create($outJp)
                            
                            $remaining = $size
                            $dataBuffer = New-Object byte[] 512
                            while ($remaining -gt 0) {
                                $bytesToRead = if ($remaining -gt 512) { 512 } else { $remaining }
                                [void]$tarFileStream.Read($dataBuffer, 0, 512)
                                $outStream.Write($dataBuffer, 0, $bytesToRead)
                                $remaining -= 512
                            }
                            $outStream.Close()
                            $jp = $outJp
                            Write-Host "script.json erfolgreich aus dem Archiv extrahiert!" -ForegroundColor Green
                            break
                        } else {
                            if ($size -gt 0) {
                                $blocks = [Math]::Ceiling($size / 512)
                                [void]$tarFileStream.Seek(($blocks * 512), [System.IO.SeekOrigin]::Current)
                            }
                        }
                    }
                    $tarFileStream.Close()
                } catch {
                    Write-Host "Fehler beim .NET-Entpacken: $_" -ForegroundColor Red
                }
            } else {
                $jp = $gewaehlteDatei
            }
            
            if (-not $jp -or -not (Test-Path $jp)) {
                [System.Windows.Forms.MessageBox]::Show("Es konnte keine gültige script.json geladen werden!", "Fehler", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
                exit
            }
            
            $raw = Get-Content $jp -Raw; if ([string]::IsNullOrWhiteSpace($raw)) { exit }
            $json = ConvertFrom-Json $raw; $skriptListe = [System.Collections.Generic.List[Object]]::new()
            
            # Zuweisungsschutz für die korrekte Erkennung von Blocklys
            function Get-SichererBlocklyXml ([string]$sourceCode) {
                if ([string]::IsNullOrWhiteSpace($sourceCode)) { return "" }
                
                if ($sourceCode -match '//([A-Za-z0-9+/=%]{50,})\s*$') {
                    try {
                        $gesichertesMatch = $Matches[1]
                        $xml = [System.Web.HttpUtility]::UrlDecode([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($gesichertesMatch)))
                        if ($xml -match '<xml') { return ($xml -replace '><', ">`r`n<").Trim() }
                    } catch {}
                }
                
                if ($sourceCode -match '(?s)(<xml.*?</xml>)') { 
                    $gesichertesXmlMatch = $Matches[1]
                    return ($gesichertesXmlMatch -replace '><', ">`r`n<").Trim() 
                }
                return ""
            }
            
            function Add-SkriptToList ([string]$id, $obj) {
                if (-not $obj -or ($obj.type -match "channel|folder")) { return }
                $saubereID = $id -replace '^script\.js\.(common\.|global\.)?', ''
                $parts = $saubereID.Split('.'); $p = "Hauptordner"
                if ($parts.Count -gt 1) { $p = $parts[0..($parts.Count-2)] -join '/' }
                $src = if ($obj.common -and $obj.common.source) { $obj.common.source } else { "" }
                $en = if ($obj.common -and $obj.common.enabled -ne $null) { $obj.common.enabled } else { $true }
                $xml = Get-SichererBlocklyXml -sourceCode $src
                $skriptListe.Add([PSCustomObject]@{
                    Name = $parts[-1]; Pfad = $p; Internal = $id; JsSource = $src; XmlSource = $xml;
                    Typ = if ($xml) { "Blockly" } else { "JavaScript" }; Status = if ($en) { "Aktiv" } else { "Deaktiviert" }
                })
            }
            
            if ($json.PSObject.Properties.Name -contains "objects") {
                foreach ($p in $json.objects.PSObject.Properties) { if ($p.Name -like "script.js.*") { Add-SkriptToList -id $p.Name -obj $p.Value } }
            } else {
                foreach ($p in $json.PSObject.Properties) { if ($p.Name -like "script.js.*") { Add-SkriptToList -id $p.Name -obj $p.Value } }
            }
            if ($skriptListe.Count -eq 0) { exit }
            # 1. FUNKTION GANZ OBEN DEFINIEREN
            function Update-Grid {
                $filterText = $txtSearch.Text.Trim()
                $ausgewaehlterTyp = $cmbType.SelectedItem.ToString()
                
                $filtered = $skriptListe | Where-Object { 
                    [string]::IsNullOrWhiteSpace($filterText) -or 
                    $_.Name.ToLower().Contains($filterText.ToLower()) -or 
                    $_.Pfad.ToLower().Contains($filterText.ToLower())
                }
                
                if ($chkHide.Checked) { $filtered = $filtered | Where-Object { $_.Status -eq "Aktiv" } }
                
                if ($ausgewaehlterTyp -eq "Nur Blockly") { $filtered = $filtered | Where-Object { $_.Typ -eq "Blockly" } }
                elseif ($ausgewaehlterTyp -eq "Nur JavaScript") { $filtered = $filtered | Where-Object { $_.Typ -eq "JavaScript" } }
                
                $dt = New-Object System.Data.DataTable
                [void]$dt.Columns.AddRange(@("Name","ioBroker-Pfad","Typ","Status","InternalID"))
                foreach ($item in $filtered) { [void]$dt.Rows.Add($item.Name, $item.Pfad, $item.Typ, $item.Status, $item.Internal) }
                
                $dgv.DataSource = $dt
                if ($dgv.Columns["InternalID"]) { $dgv.Columns["InternalID"].Visible = $false }
                $lblCount.Text = "Gefundene Skripte (aktueller Filter): " + $filtered.Count
                $lblPrev.Text = "Code-Vorschau (0 Skripte ausgewählt):"
            }
            
            # NEU: Die Export-Logik als eigene Funktion, damit das Fenster offen bleiben kann!
            function Start-ScriptExport ([string]$modus) {
                $ausIDs = @()
                if ($modus -eq "all") {
                    foreach ($row in $dgv.Rows) { $ausIDs += [string]$row.Cells["InternalID"].Value }
                } else {
                    if ($dgv.SelectedRows.Count -eq 0) {
                        [System.Windows.Forms.MessageBox]::Show("Bitte wähle zuerst mindestens ein Skript aus der Liste aus!", "Hinweis", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
                        return
                    }
                    foreach ($row in $dgv.SelectedRows) { $ausIDs += [string]$row.Cells["InternalID"].Value }
                }
                
                $folderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
                if ($folderDialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { return }
                
                $root = Join-Path $folderDialog.SelectedPath ("ioBroker_Backup_" + (Get-Date -Format "yyyy-MM-dd_HH-mm"))
                New-Item -ItemType Directory -Force -Path $root | Out-Null
                
                foreach ($targetID in $ausIDs) {
                    $scriptData = $skriptListe | Where-Object { $_.Internal -eq $targetID }; if (-not $scriptData) { continue }
                    $folder = if ($scriptData.Pfad -ne "Hauptordner") { Join-Path $root ($scriptData.Pfad -replace '/', '\') } else { $root }
                    if ($scriptData.Status -eq "Deaktiviert") { $folder = Join-Path $folder "_DEAKTIVIERT" }
                    New-Item -ItemType Directory -Force -Path $folder | Out-Null
                    if ($scriptData.Typ -eq "Blockly" -and $scriptData.XmlSource) {
                        $scriptData.XmlSource | Set-Content (Join-Path $folder ($scriptData.Name + ".xml")) -Encoding UTF8
                        $cleanJs = $scriptData.JsSource
                        if ($cleanJs -match '//([A-Za-z0-9+/=%]{50,})\s*$') { $cleanJs = $cleanJs.Replace($Matches, "").Trim() }
                        if ($cleanJs -match '(?s)<xml.*?></xml>') { $cleanJs = $cleanJs.Replace($Matches, "").Trim() }
                        $cleanJs | Set-Content (Join-Path $folder ($scriptData.Name + ".js")) -Encoding UTF8
                    } else { $scriptData.JsSource | Set-Content (Join-Path $folder ($scriptData.Name + ".js")) -Encoding UTF8 }
                }
                
                # Erfolgsmeldung anzeigen und Ordner öffnen, ohne das Tool zu schließen!
                Invoke-Item $root
                [System.Windows.Forms.MessageBox]::Show("Export von $($ausIDs.Count) Skripten erfolgreich abgeschlossen!", "Fertig", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
            }
            
            # 2. FENSTER ERSTELLEN
            $f = New-Object System.Windows.Forms.Form
            $f.Text = "ioBroker Script Extractor"
            $f.Size = New-Object System.Drawing.Size(950, 800)
            $f.StartPosition = "CenterScreen"
            
            $pnlTop = New-Object System.Windows.Forms.Panel
            $pnlTop.Dock = "Top"
            $pnlTop.Height = 75
            $f.Controls.Add($pnlTop)
            
            $lblSrc = New-Object System.Windows.Forms.Label -Property @{Text="Suchen:"; Location=New-Object System.Drawing.Point(10,18); Size=New-Object System.Drawing.Size(50,20)}
            $txtSearch = New-Object System.Windows.Forms.TextBox -Property @{Location=New-Object System.Drawing.Point(65,15); Size=New-Object System.Drawing.Size(410,20)}
            $chkHide = New-Object System.Windows.Forms.CheckBox -Property @{Text="Deaktivierte ausblenden"; Location=New-Object System.Drawing.Point(490,13); Size=New-Object System.Drawing.Size(170,24)}
            
            $cmbType = New-Object System.Windows.Forms.ComboBox
            $cmbType.Location = New-Object System.Drawing.Point(670, 13)
            $cmbType.Size = New-Object System.Drawing.Size(150, 24)
            $cmbType.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
            [void]$cmbType.Items.AddRange(@("Alle Typen", "Nur Blockly", "Nur JavaScript"))
            $cmbType.SelectedIndex = 0
            
            $lblCount = New-Object System.Windows.Forms.Label
            $lblCount.Text = "Gefundene Skripte: 0"
            $lblCount.Location = New-Object System.Drawing.Point(10, 48)
            $lblCount.Size = New-Object System.Drawing.Size(500, 20)
            $lblCount.Font = New-Object System.Drawing.Font("Arial", 9, [System.Drawing.FontStyle]::Bold)
            
            $pnlTop.Controls.AddRange(@($lblSrc, $txtSearch, $chkHide, $cmbType, $lblCount))
            
            $pnlBottom = New-Object System.Windows.Forms.Panel
            $pnlBottom.Dock = "Bottom"
            $pnlBottom.Height = 55
            $f.Controls.Add($pnlBottom)
            
            $btnClip = New-Object System.Windows.Forms.Button -Property @{Text="In Zwischenablage"; Location=New-Object System.Drawing.Point(10,10); Size=New-Object System.Drawing.Size(160,35)}
            $btnAll = New-Object System.Windows.Forms.Button -Property @{Text="Alle exportieren"; Location=New-Object System.Drawing.Point(180,10); Size=New-Object System.Drawing.Size(150,35)}
            $btnOk = New-Object System.Windows.Forms.Button -Property @{Text="Ausgewaehlte exportieren"; Location=New-Object System.Drawing.Point(500,10); Size=New-Object System.Drawing.Size(160,35)}
            $btnCan = New-Object System.Windows.Forms.Button -Property @{Text="Schliessen"; Location=New-Object System.Drawing.Point(670,10); Size=New-Object System.Drawing.Size(150,35)}
            $pnlBottom.Controls.AddRange(@($btnClip, $btnAll, $btnOk, $btnCan))
            
            $dgv = New-Object System.Windows.Forms.DataGridView
            $dgv.Location = New-Object System.Drawing.Point(10, 80)
            $dgv.Size = New-Object System.Drawing.Size(915, 260)
            $dgv.Anchor = "Top, Left, Right"
            $dgv.AutoSizeColumnsMode = "Fill"
            $dgv.SelectionMode = "FullRowSelect"
            $dgv.MultiSelect = $true
            $dgv.AllowUserToAddRows = $false
            $dgv.ReadOnly = $true
            $dgv.RowHeadersVisible = $false
            $f.Controls.Add($dgv)
            
            $lblPrev = New-Object System.Windows.Forms.Label
            $lblPrev.Text = "Code-Vorschau:"
            $lblPrev.Location = New-Object System.Drawing.Point(10, 355)
            $lblPrev.Size = New-Object System.Drawing.Size(500, 20)
            $lblPrev.Anchor = "Top, Left"
            $f.Controls.Add($lblPrev)
            
            $txtPrev = New-Object System.Windows.Forms.RichTextBox
            $txtPrev.Location = New-Object System.Drawing.Point(10, 380)
            $txtPrev.Size = New-Object System.Drawing.Size(915, 310)
            $txtPrev.Anchor = "Top, Bottom, Left, Right"
            $txtPrev.Font = New-Object System.Drawing.Font("Consolas", 10)
            $txtPrev.ReadOnly = $true
            $txtPrev.WordWrap = $false
            $f.Controls.Add($txtPrev)
            
            # 3. EVENTS VERDRAHTEN
            $txtSearch.Add_TextChanged({ Update-Grid })
            $chkHide.Add_CheckedChanged({ Update-Grid })
            $cmbType.Add_SelectedIndexChanged({ Update-Grid })
            
            $dgv.Add_SelectionChanged({
                $anz = $dgv.SelectedRows.Count
                if ($anz -gt 0) {
                    $selectedRow = $dgv.SelectedRows | Select-Object -First 1; $selID = [string]$selectedRow.Cells["InternalID"].Value
                    if (-not [string]::IsNullOrEmpty($selID)) {
                        $m = $skriptListe | Where-Object { $_.Internal -eq $selID }
                        if ($m) { $txtPrev.Text = if ($m.Typ -eq "Blockly" -and $m.XmlSource) { $m.XmlSource } else { $m.JsSource } }
                    }
                    $lblPrev.Text = if ($anz -eq 1) { "Code-Vorschau (1 Skript ausgewählt):" } else { "Code-Vorschau ($anz Skripte ausgewählt):" }
                } else { $txtPrev.Text = ""; $lblPrev.Text = "Code-Vorschau (0 Skripte ausgewählt):" }
            })
            
            $btnClip.Add_Click({
                if (-not [string]::IsNullOrEmpty($txtPrev.Text)) {
                    [System.Windows.Forms.Clipboard]::SetText($txtPrev.Text)
                    $global:oldText = $btnClip.Text; $btnClip.Text = "Kopiert!"; $btnClip.Enabled = $false
                    $global:clipTimer = New-Object System.Windows.Forms.Timer -Property @{ Interval = 1500 }
                    $global:clipTimer.Add_Tick({ $btnClip.Text = $global:oldText; $btnClip.Enabled = $true; $global:clipTimer.Stop(); $global:clipTimer.Dispose() }); $global:clipTimer.Start()
                }
            })
            
            # KORREKTUR: Knöpfe rufen jetzt die Funktion auf und halten das Fenster offen!
            $btnAll.Add_Click({ Start-ScriptExport -modus "all" })
            $btnOk.Add_Click({ Start-ScriptExport -modus "selected" })
            $btnCan.Add_Click({ $f.Close() })
            
            # Grid initial befüllen & Fenster starten
            Update-Grid
            [void]$f.ShowDialog()
            
            # Nach dem manuellen Schließen des Fensters den Temp-Ordner aufräumen
            if (Test-Path (Join-Path $so "temp_extract")) { Remove-Item (Join-Path $so "temp_extract") -Recurse -Force -EA SilentlyContinue }
            

            Powershell öffnen

            • cd C:\iobroker_Script_recover
            • .\ioBroker_Script_Extractor.ps1
            • javascripts_XXXXXXXX_backupiobroker.tar.gz oder script.json aus dem Backup von Backitup auswählen.

            Screenshot 2026-07-20 142038.jpg

            Gruß

            1 Antwort Letzte Antwort
            1
            • Michael SchmittM Online
              Michael SchmittM Online
              Michael Schmitt
              schrieb am zuletzt editiert von Michael Schmitt
              #6

              Hi,
              hab das ganze nun auch als .exe, aber weiß nicht ob man sowas hier posten/anbieten darf.

              Edit: nur noch "ioBroker_Script_Extractor.exe" starten und die Javascript Backupdatei oder script.json auswählen und fertig. Keine Datei erstellen oder PowerShell und Links eingeben.

              1 Antwort Letzte Antwort
              0
              • Michael SchmittM Online
                Michael SchmittM Online
                Michael Schmitt
                schrieb am zuletzt editiert von
                #7

                Dann stell ich das mal hier rein. Falls nicht erwünscht dann löschen.

                ioBroker_Script_Extractor.exe (gepackte Version) = ioBroker_Script_Extractor.zip

                1 Antwort Letzte Antwort
                0
                • D Offline
                  D Offline
                  dodi666
                  schrieb zuletzt editiert von dodi666
                  #8

                  Sehr cool, probiere ich heute Abend mal aus. Aber gibt es dafür nicht auch einen Adapter?
                  test-adapter-script-restore

                  Michael SchmittM 1 Antwort Letzte Antwort
                  1
                  • D dodi666

                    Sehr cool, probiere ich heute Abend mal aus. Aber gibt es dafür nicht auch einen Adapter?
                    test-adapter-script-restore

                    Michael SchmittM Online
                    Michael SchmittM Online
                    Michael Schmitt
                    schrieb zuletzt editiert von Michael Schmitt
                    #9

                    @dodi666 sagte:

                    Sehr cool, probiere ich heute Abend mal aus. Aber gibt es dafür nicht auch einen Adapter?

                    Ja gibt es, aber das wird man ja nicht sooo oft brauchen (hoffe ich) und dann möchte ich mir nicht extra einen Adapter installieren. Das Tool sollte dafür reichen. (für meine Zwecke)

                    fuzzy1955F 1 Antwort Letzte Antwort
                    0
                    • David G.D Online
                      David G.D Online
                      David G.
                      schrieb zuletzt editiert von David G.
                      #10

                      Ich habe aktuell einen Adapter für das Thema im Latest.

                      Denke, dass er bald im stable ankommen wird.
                      https://forum.iobroker.net/topic/84506/test-adapter-script-restore-v0.1.11

                      Aber auch ein Cooler Ansatz für Windows User.

                      EDIT

                      Oh, wurde oben ja schon drauf hingewiesen...

                      @Michael-Schmitt sagte:

                      Ja gibt es, aber das wird man ja nicht sooo oft brauchen (hoffe ich) und dann möchte ich mir nicht extra einen Adapter installieren. Das Tool sollte dafür reichen. (für meine Zwecke)

                      Ich hab mir schon oft Scripte zerschossen, weil ich vergessen hab eine Kopie zu erstellen 😂

                      Zeigt eure Lovelace-Visualisierung klick
                      (Auch ideal um sich Anregungen zu holen)

                      Meine Tabellen für eure Visualisierung klick

                      1 Antwort Letzte Antwort
                      0
                      • Michael SchmittM Michael Schmitt

                        @dodi666 sagte:

                        Sehr cool, probiere ich heute Abend mal aus. Aber gibt es dafür nicht auch einen Adapter?

                        Ja gibt es, aber das wird man ja nicht sooo oft brauchen (hoffe ich) und dann möchte ich mir nicht extra einen Adapter installieren. Das Tool sollte dafür reichen. (für meine Zwecke)

                        fuzzy1955F Online
                        fuzzy1955F Online
                        fuzzy1955
                        schrieb zuletzt editiert von
                        #11

                        @Michael-Schmitt sagte:

                        Das Tool sollte dafür reichen

                        Hi Michael,

                        dein Tool für Windows eignet sich hervorragend zum Ansehen und Herauskopieren von Skripts bzw. -teilen aus Backups. Ich hab's ausprobiert. Danke für deine Ideen!

                        Gruß, Fuzzy

                        Smart-Home-Automation unter IOB auf RASPI5, 16GB RAM, 50GB SSD, MariaSQL, VIS-2.
                        Anfang 2025 war ich Newbie in Sachen RaspBerry, Linux und IOB.
                        Jetzt läuft alles konstant gut und ich gebe gern meine Erfahrungen und mein Wissen an die Forums-User/Innen weiter.
                        Großes Danke an alle tatkräftigen Helfer im Forum!

                        1 Antwort Letzte Antwort
                        1

                        Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.

                        Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.

                        Mit deinem Input könnte dieser Beitrag noch besser werden 💗

                        Registrieren Anmelden
                        Antworten
                        • In einem neuen Thema antworten
                        Anmelden zum Antworten
                        • Älteste zuerst
                        • Neuste zuerst
                        • Meiste Stimmen


                        Support us

                        ioBroker
                        Community Adapters
                        Donate

                        394

                        Online

                        33.0k

                        Benutzer

                        83.4k

                        Themen

                        1.3m

                        Beiträge
                        Community
                        Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                        ioBroker Community 2014-2026
                        logo
                        • Anmelden

                        • Du hast noch kein Konto? Registrieren

                        • Anmelden oder registrieren, um zu suchen
                        • Erster Beitrag
                          Letzter Beitrag
                        0
                        • Home
                        • Aktuell
                        • Tags
                        • Ungelesen 0
                        • Kategorien
                        • Unreplied
                        • Beliebt
                        • GitHub
                        • Docu
                        • Hilfe