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
    1.1k

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

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

Blockly + Javascripte aus einer script.json extrahieren

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
24 Beiträge 8 Kommentatoren 1.4k Aufrufe 8 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
    #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 ;)

    F vowillV 2 Antworten Letzte Antwort
    1
    • 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 ;)

      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

            .............................

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

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

              1 Antwort Letzte Antwort
              0
              • D Offline
                D Offline
                dodi666
                schrieb am 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 am 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 am 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 am 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
                      • Michael SchmittM Online
                        Michael SchmittM Online
                        Michael Schmitt
                        schrieb am zuletzt editiert von
                        #12

                        Hi, gern geschehen. Freu mich wenn es nützlich ist ;)

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

                          Hier die neueste Version

                          ioBroker_Script_Extractor.ps1

                          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 = ""
                          $tmp = Join-Path $so "temp_extract"
                          
                          try {
                              # 1. Dateifenster beim Start öffnen
                              $fd = New-Object System.Windows.Forms.OpenFileDialog
                              $fd.InitialDirectory = if (Test-Path $so) { $so } else { [Environment]::GetFolderPath("Desktop") }
                              $fd.Filter = "ioBroker Dateien (*.json;*.tar.gz)|*.json;*.tar.gz"
                              $fd.Title = "Bitte waehle deine script.json ODER eine ioBroker .tar.gz Datei aus"
                          
                              if ($fd.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { exit }
                              $gewaehlteDatei = $fd.FileName
                          
                              # Wenn eine .tar.gz ausgewählt wurde, entpacken im Speicher
                              if ($gewaehlteDatei.ToLower().EndsWith(".tar.gz")) {
                                  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)
                                      $gz = New-Object System.IO.Compression.GZipStream($inFile.OpenRead(), [System.IO.Compression.CompressionMode]::Decompress)
                                      
                                      $tarPath = Join-Path $tmp "temp_backup.tar"
                                      $tarOut = [System.IO.File]::Create($tarPath)
                                      $gz.CopyTo($tarOut)
                                      $tarOut.Close()
                                      $gz.Close()
                                      
                                      $ts = [System.IO.File]::OpenRead($tarPath)
                                      $buf = New-Object byte[] 512
                                      
                                      while ($ts.Read($buf, 0, 512) -eq 512) {
                                          $name = [System.Text.Encoding]::ASCII.GetString($buf, 0, 100).Trim(([char]0))
                                          $szStr = [System.Text.Encoding]::ASCII.GetString($buf, 124, 12).Trim()
                                          $sz = 0
                                          if ($szStr) { $sz = [Convert]::ToInt64($szStr, 8) }
                                          
                                          if ($name.ToLower().EndsWith("script.json")) {
                                              $outStream = [System.IO.File]::Create((Join-Path $tmp "script.json"))
                                              $rem = $sz
                                              $dBuf = New-Object byte[] 512
                                              while ($rem -gt 0) {
                                                  $read = 512
                                                  if ($rem -lt 512) { $read = $rem }
                                                  [void]$ts.Read($dBuf, 0, 512)
                                                  $outStream.Write($dBuf, 0, $read)
                                                  $rem -= 512
                                              }
                                              $outStream.Close()
                                              $jp = Join-Path $tmp "script.json"
                                              break
                                          } else { 
                                              if ($sz -gt 0) { 
                                                  $blocks = [Math]::Ceiling($sz / 512)
                                                  [void]$ts.Seek(($blocks * 512), [System.IO.SeekOrigin]::Current) 
                                              } 
                                          }
                                      }
                                      $ts.Close()
                                  } catch {}
                              } else {
                                  $jp = $gewaehlteDatei
                              }
                          
                              if (-not $jp -or -not (Test-Path $jp)) { exit }
                          
                              $raw = Get-Content $jp -Raw -Encoding UTF8
                              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) { return }
                                  if ($obj.type -match "channel|folder") { return }
                                  $parts = ($id -replace '^script\.js\.(common\.|global\.)?', '').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 }
                                  $en = $true
                                  if ($obj.common -and $obj.common.enabled -ne $null) { $en = $obj.common.enabled }
                                  $xml = Get-SichererBlocklyXml -sourceCode $src
                                  $typText = "JavaScript"
                                  if ($xml) { $typText = "Blockly" }
                                  $statText = "Deaktiviert"
                                  if ($en) { $statText = "Aktiv" }
                                  $skriptListe.Add([PSCustomObject]@{
                                      Name      = $parts[-1]
                                      Pfad      = $p
                                      Internal  = $id
                                      JsSource  = $src
                                      XmlSource = $xml
                                      Typ       = $typText
                                      Status    = $statText
                                  })
                              }
                          
                              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 }
                          
                              function Update-Grid {
                                  $filterText = $txtSearch.Text.Trim()
                                  $ausTyp = $cmbType.SelectedItem.ToString()
                                  $suchenInCode = $rbSearchDp.Checked
                                  
                                  $filtered = $skriptListe | Where-Object { 
                                      if ([string]::IsNullOrWhiteSpace($filterText)) {
                                          $true
                                      } else {
                                          if ($suchenInCode) {
                                              # Durchsucht JavaScript-Quellcode und Blockly-XML nach dem Datenpunkt/Begriff
                                              $_.JsSource.ToLower().Contains($filterText.ToLower()) -or 
                                              ($_.XmlSource -and $_.XmlSource.ToLower().Contains($filterText.ToLower()))
                                          } else {
                                              # Standard-Suche nach Name oder Pfad
                                              $_.Name.ToLower().Contains($filterText.ToLower()) -or 
                                              $_.Pfad.ToLower().Contains($filterText.ToLower())
                                          }
                                      }
                                  }
                                  
                                  if ($chkHide.Checked) { 
                                      $filtered = $filtered | Where-Object { $_.Status -eq "Aktiv" } 
                                  }
                                  if ($ausTyp -eq "Nur Blockly") { 
                                      $filtered = $filtered | Where-Object { $_.Typ -eq "Blockly" } 
                                  }
                                  elseif ($ausTyp -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 }
                                  
                                  $modusText = if ($suchenInCode) { "Treffer für Datenpunkt/Code" } else { "Gefundene Skripte" }
                                  $lblCount.Text = "$modusText (aktueller Filter): " + $filtered.Count
                                  $lblPrev.Text = "Code-Vorschau (0 Skripte ausgewählt):"
                              }
                          
                              function Start-ScriptExport ([string]$modus) {
                                  $ausIDs = @()
                                  if ($modus -eq "all") { 
                                      foreach ($row in $dgv.Rows) { 
                                          if ($row.Cells["InternalID"].Value) { $ausIDs += [string]$row.Cells["InternalID"].Value }
                                      } 
                                  } else { 
                                      if ($dgv.SelectedRows.Count -eq 0) { return }
                                      foreach ($row in $dgv.SelectedRows) { 
                                          if ($row.Cells["InternalID"].Value) { $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
                                  
                                  $utf8NoBom = New-Object System.Text.UTF8Encoding $false
                          
                                  foreach ($targetID in $ausIDs) {
                                      $scriptData = $skriptListe | Where-Object { $_.Internal -eq $targetID }
                                      if (-not $scriptData) { continue }
                                      $folder = $root
                                      if ($scriptData.Pfad -ne "Hauptordner") { $folder = Join-Path $root ($scriptData.Pfad -replace '/', '\') }
                                      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) {
                                          [System.IO.File]::WriteAllText((Join-Path $folder ($scriptData.Name + ".xml")), $scriptData.XmlSource, $utf8NoBom)
                                          $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() }
                                          [System.IO.File]::WriteAllText((Join-Path $folder ($scriptData.Name + ".js")), $cleanJs, $utf8NoBom)
                                      } else { 
                                          [System.IO.File]::WriteAllText((Join-Path $folder ($scriptData.Name + ".js")), $scriptData.JsSource, $utf8NoBom)
                                      }
                                  }
                                  Invoke-Item $root
                                  [System.Windows.Forms.MessageBox]::Show("Export von $($ausIDs.Count) Skripte(n) erfolgreich abgeschlossen!", "Fertig", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
                              }
                          
                              # GUI Definition
                              $f = New-Object System.Windows.Forms.Form
                              $f.Text = "ioBroker Script Extractor & Search"
                              $f.Size = New-Object System.Drawing.Size(960, 830)
                              $f.StartPosition = "CenterScreen"
                          
                              $pnlTop = New-Object System.Windows.Forms.Panel
                              $pnlTop.Dock = "Top"
                              $pnlTop.Height = 100
                              $f.Controls.Add($pnlTop)
                          
                              # Suchmodus Radio-Buttons
                              $grpMode = New-Object System.Windows.Forms.GroupBox -Property @{Text="Suchmodus"; Location=New-Object System.Drawing.Point(10,5); Size=New-Object System.Drawing.Size(465,40)}
                              $rbSearchName = New-Object System.Windows.Forms.RadioButton -Property @{Text="Skripte (Name/Pfad)"; Location=New-Object System.Drawing.Point(10,15); Size=New-Object System.Drawing.Size(150,20); Checked=$true}
                              $rbSearchDp = New-Object System.Windows.Forms.RadioButton -Property @{Text="In Skripten nach Datenpunkt / Code suchen"; Location=New-Object System.Drawing.Point(170,15); Size=New-Object System.Drawing.Size(280,20)}
                              $grpMode.Controls.AddRange(@($rbSearchName, $rbSearchDp))
                          
                              $lblSrc = New-Object System.Windows.Forms.Label -Property @{Text="Suchen:"; Location=New-Object System.Drawing.Point(10,53); Size=New-Object System.Drawing.Size(50,20)}
                              $txtSearch = New-Object System.Windows.Forms.TextBox -Property @{Location=New-Object System.Drawing.Point(65,50); 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,48); Size=New-Object System.Drawing.Size(170,24)}
                          
                              $cmbType = New-Object System.Windows.Forms.ComboBox
                              $cmbType.Location = New-Object System.Drawing.Point(670, 48)
                              $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, 78)
                              $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(@($grpMode, $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 -Property @{Location=New-Object System.Drawing.Point(10, 105); Size=New-Object System.Drawing.Size(925, 235); Anchor="Top, Left, Right"; AutoSizeColumnsMode="Fill"; SelectionMode="FullRowSelect"; MultiSelect=$true; AllowUserToAddRows=$false; ReadOnly=$true; 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, 345)
                              $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, 370)
                              $txtPrev.Size = New-Object System.Drawing.Size(925, 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)
                          
                              $txtSearch.Add_TextChanged({ Update-Grid })
                              $chkHide.Add_CheckedChanged({ Update-Grid })
                              $cmbType.Add_SelectedIndexChanged({ Update-Grid })
                              $rbSearchName.Add_CheckedChanged({ Update-Grid })
                              $rbSearchDp.Add_CheckedChanged({ Update-Grid })
                          
                              $dgv.Add_SelectionChanged({
                                  $anz = $dgv.SelectedRows.Count
                                  if ($anz -gt 0) {
                                      $selectedRow = $dgv.SelectedRows | Select-Object -First 1
                                      if ($selectedRow.Cells["InternalID"] -and $selectedRow.Cells["InternalID"].Value) {
                                          $selID = [string]$selectedRow.Cells["InternalID"].Value
                                          $m = $skriptListe | Where-Object { $_.Internal -eq $selID }
                                          if ($m) { 
                                              $codeText = if ($m.Typ -eq "Blockly" -and $m.XmlSource) { $m.XmlSource } else { $m.JsSource }
                                              
                                              # Vorschau zurücksetzen
                                              $txtPrev.Text = $codeText
                                              $txtPrev.SelectAll()
                                              $txtPrev.SelectionBackColor = [System.Drawing.Color]::White
                                              $txtPrev.SelectionColor = [System.Drawing.Color]::Black
                                              $txtPrev.DeselectAll()
                                              
                                              # Bei Datenpunktsuche Treffer gelb markieren & anspringen
                                              $searchTerm = $txtSearch.Text.Trim()
                                              if ($rbSearchDp.Checked -and -not [string]::IsNullOrWhiteSpace($searchTerm)) {
                                                  $idx = $codeText.IndexOf($searchTerm, [System.StringComparison]::OrdinalIgnoreCase)
                                                  if ($idx -ge 0) {
                                                      $txtPrev.Select($idx, $searchTerm.Length)
                                                      # Korrigierte Eigenschaft SelectionBackColor für WinForms RichTextBox
                                                      $txtPrev.SelectionBackColor = [System.Drawing.Color]::Yellow
                                                      $txtPrev.SelectionColor = [System.Drawing.Color]::Black
                                                      $txtPrev.ScrollToCaret()
                                                  }
                                              }
                                          }
                                      }
                                      $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()
                                  }
                              })
                          
                              $btnAll.Add_Click({ Start-ScriptExport -modus "all" })
                              $btnOk.Add_Click({ Start-ScriptExport -modus "selected" })
                              $btnCan.Add_Click({ $f.Close() })
                          
                              Update-Grid
                              [void]$f.ShowDialog()
                          
                          } finally {
                              if (Test-Path $tmp) { Remove-Item $tmp -Recurse -Force -EA SilentlyContinue }
                          }
                          

                          damit kann man nun auch nach Datenpunkten in den Scripten suchen und diese werden dann aufgelistet.

                          333.jpg

                          ioBroker_Script_Extractor.zip

                          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 ;)

                            vowillV Offline
                            vowillV Offline
                            vowill
                            schrieb am zuletzt editiert von
                            #14

                            @Michael-Schmitt sagte:

                            Hier die EXE direkt für Windows

                            Hm. Dieses Program legt bei mir dann einen Ordner "temp_extract" an, in dem das File "temp_backup.tar" abgelegt wird. Aber wo finde ich nun die Skripte?

                            ioBroker im lxc auf NUC / Aktive Instanzen: 38

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

                              @vowill werden dir den in der GUI die Scripte angezeigt ?
                              444.jpg

                              du kannst unten auf

                              • in Zwischenablage - kopiert den code der unten steht in die Zwischenablage
                              • Alle exportieren - hiermit werden alle gefundenen Scripte exportiert
                              • Ausgewählte exportieren - die Scripte die blau unterlegt sind werden exportiert
                              1 Antwort Letzte Antwort
                              0
                              • vowillV Offline
                                vowillV Offline
                                vowill
                                schrieb am zuletzt editiert von
                                #16

                                @michael-schmitt Danke für deine schnelle Rückmeldung. Vermutlich läuft bei mir das Programm nicht richtig: Es legt nur die schon genannte Datei 'temp_backup.tar' an, ohne dass sich ein Fenster öffnet. Vielleicht braucht das exe noch irgendwelche Zugriffsrechte ...

                                ioBroker im lxc auf NUC / Aktive Instanzen: 38

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

                                  du hast aber die zb "javascript_2026_08_09-03_37_10_backupiobroker.tar.gz" auf deinem PC. wenn die Dateien angelegt werden sollte das Programm auch laufen. Ist kein Symbol in der Taskleiste ?

                                  1.jpg

                                  S vowillV 2 Antworten Letzte Antwort
                                  0
                                  • Michael SchmittM Michael Schmitt

                                    du hast aber die zb "javascript_2026_08_09-03_37_10_backupiobroker.tar.gz" auf deinem PC. wenn die Dateien angelegt werden sollte das Programm auch laufen. Ist kein Symbol in der Taskleiste ?

                                    1.jpg

                                    S Abwesend
                                    S Abwesend
                                    Shadowhunter23
                                    schrieb am zuletzt editiert von
                                    #18

                                    @Michael-Schmitt
                                    Mit welcher KI baust du das Tool?

                                    Proxmox 9 HA-Cluster mit 3x HP prodesk 400 G6 i5
                                    ioBroker Backup Analyzer

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

                                      Teilweise Gemini und ChatGPT

                                      EDIT: es ist jeder Willkommen die Idee weiter zu spinnen und zu verbessern. Ich mach das nur aus Spaß an der Freude und aus der Not das ein oder andere mal ein Script zerschossen zu haben. Also Leute mit bezahlter Ki zaubern da sicher was besseres ;)

                                      Für mich reichts

                                      S 1 Antwort Letzte Antwort
                                      0
                                      • Michael SchmittM Michael Schmitt

                                        du hast aber die zb "javascript_2026_08_09-03_37_10_backupiobroker.tar.gz" auf deinem PC. wenn die Dateien angelegt werden sollte das Programm auch laufen. Ist kein Symbol in der Taskleiste ?

                                        1.jpg

                                        vowillV Offline
                                        vowillV Offline
                                        vowill
                                        schrieb am zuletzt editiert von vowill
                                        #20

                                        @Michael-Schmitt sagte:

                                        du hast aber die zb "iobroker_2026_08_09-03_37_10_backupiobroker.tar.gz" auf deinem PC. wenn die Dateien angelegt werden sollte das Programm auch laufen. Ist kein Symbol in der Taskleiste ?

                                        Ja, Backup-File ist auf dem PC im gleichen Directory. Beim Starten des .exe mosert erst Windows (11) wg. unbekannter App; nach dem Start öffnet sich das Fenster zur Dateiauswahl. Nach der Auswahl wird die o.g. Datei 'temp_backup.tar' angelegt und das Programm beendet sich. Die ganze Zeit erscheint kein Programmsymbol in der Taskleiste.

                                        Nachtrag: Wenn ich das ps1 anlege und es in PowerShell öffne, kommt die Fehlermeldung

                                        • CategoryInfo: Sicherheitsfehler: (:) [], PSSecurityException
                                        • FullyQualifiedErrorId : UnauthorizedAccess

                                        Noch ein Nachtrag: In PS musste ich die Ausführungsrichtlinie ändern (auf 'Bypass' oder 'Unrestricted'). Dann läuft das Skript los.
                                        Allerdings findet er in meinem Backup-File keine Datei script.json (die habe ich nach dem Entpacken auch nicht gefunden). Meine Skripte liegen im Backup wohl alle in der Datei "opjects.jsonl" 😕

                                        ioBroker im lxc auf NUC / Aktive Instanzen: 38

                                        1 Antwort Letzte Antwort
                                        0
                                        • NashraN Offline
                                          NashraN Offline
                                          Nashra
                                          Most Active Forum Testing
                                          schrieb am zuletzt editiert von
                                          #21

                                          @michael-schmitt
                                          habe die exe mal gestartet, funktioniert bei mir.
                                          Gut gemacht 👍

                                          Gruß Ralf
                                          Mir egal, wer Dein Vater ist! Wenn ich hier angel, wird nicht übers Wasser gelaufen!!

                                          Benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                                          1 Antwort Letzte Antwort
                                          0

                                          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

                                          360

                                          Online

                                          33.0k

                                          Benutzer

                                          83.6k

                                          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