Correction README.md
Ajout modification du fichier Project.gfx
This commit is contained in:
@@ -244,6 +244,7 @@ elseif ($Action -eq "Write") {
|
||||
|
||||
# Associer par index sequentiel : device[0] -> DeviceId_1, device[1] -> DeviceId_2, etc.
|
||||
$xmlFiles = @()
|
||||
$deviceIdMap = @{} # Paires ancien -> nouveau pour mise a jour GFx
|
||||
for ($i = 0; $i -lt $existingDevices.Count; $i++) {
|
||||
$seqIndex = $i + 1
|
||||
$deviceIdCol = "DeviceId_$seqIndex"
|
||||
@@ -281,6 +282,11 @@ elseif ($Action -eq "Write") {
|
||||
Content = $result.ModifiedXml
|
||||
}
|
||||
|
||||
# Collecter la paire ancien -> nouveau pour le GFx
|
||||
if ($result.OldDeviceId -ne $newDeviceId) {
|
||||
$deviceIdMap[$result.OldDeviceId] = $newDeviceId
|
||||
}
|
||||
|
||||
Update-Stats -Counter DevicesProcessed
|
||||
Write-Log -Message "[$hostname] Device $($device.Name) : $($result.OldDeviceId) -> $newDeviceId" -Level INFO
|
||||
}
|
||||
@@ -290,7 +296,7 @@ elseif ($Action -eq "Write") {
|
||||
continue
|
||||
}
|
||||
|
||||
# Creer le ZIP et l'envoyer
|
||||
# Creer le ZIP enocean et l'envoyer
|
||||
$zipBytes = New-EnoceanZip -XmlFiles $xmlFiles
|
||||
$zipFilename = Get-ZipFilename -ZipBytes $zipBytes
|
||||
|
||||
@@ -304,7 +310,42 @@ elseif ($Action -eq "Write") {
|
||||
-Username $creds.Username `
|
||||
-Password $creds.Password
|
||||
|
||||
Write-Log -Message "[$hostname] Configuration envoyee avec succes" -Level SUCCESS
|
||||
Write-Log -Message "[$hostname] Configuration enocean envoyee avec succes" -Level SUCCESS
|
||||
|
||||
# Mise a jour du Project.gfx (fichier source du programme)
|
||||
if ($deviceIdMap.Count -gt 0) {
|
||||
Write-Log -Message "[$hostname] Mise a jour du Project.gfx..." -Level INFO
|
||||
|
||||
$gfxBytes = Invoke-EnoceanGet `
|
||||
-BaseUrl $baseUrl `
|
||||
-ApiBasePath $apiBasePath `
|
||||
-ResourcePath "files/common/localDevice/project/Project.gfx?encode=bin" `
|
||||
-Username $creds.Username `
|
||||
-Password $creds.Password `
|
||||
-RawBytes
|
||||
|
||||
$mainXml = Read-GfxMainXml -GfxBytes $gfxBytes
|
||||
|
||||
$gfxResult = Update-GfxDeviceIds -MainXmlContent $mainXml -DeviceIdMap $deviceIdMap
|
||||
|
||||
if ($gfxResult.ReplaceCount -gt 0) {
|
||||
$newGfxBytes = Update-GfxZip -GfxBytes $gfxBytes -ModifiedMainXml $gfxResult.ModifiedXml
|
||||
|
||||
Send-MultipartFile `
|
||||
-BaseUrl $baseUrl `
|
||||
-ApiBasePath $apiBasePath `
|
||||
-ResourcePath "files/common/localDevice/project" `
|
||||
-FileBytes $newGfxBytes `
|
||||
-Filename "Project.gfx" `
|
||||
-Username $creds.Username `
|
||||
-Password $creds.Password
|
||||
|
||||
Write-Log -Message "[$hostname] Project.gfx mis a jour ($($gfxResult.ReplaceCount) DeviceId modifie(s))" -Level SUCCESS
|
||||
}
|
||||
else {
|
||||
Write-Log -Message "[$hostname] Project.gfx : aucun DeviceId trouve a modifier" -Level WARN
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Log -Message "[$hostname] ERREUR : $($_.Exception.Message)" -Level ERROR
|
||||
|
||||
@@ -89,7 +89,7 @@ Avec un mot de passe :
|
||||
### Action Write — Écrire les DeviceId sur les automates
|
||||
|
||||
```powershell
|
||||
.\EnoceanCLI.ps1 -Action Write -CsvInput ".\enocean_2026-03-03_23h07.csv" -Password "MonMotDePasse"
|
||||
.\EnoceanCLI.ps1 -Action Write -CsvInput ".\enocean_2026-03-04_10h07.csv" -Password "MonMotDePasse"
|
||||
```
|
||||
|
||||
**Ce qui se passe** :
|
||||
@@ -194,7 +194,7 @@ Relancer un Read pour confirmer que les DeviceId ont bien changé :
|
||||
Chaque exécution génère un fichier log dans le dossier courant :
|
||||
|
||||
```
|
||||
enocean_2026-03-04_14h30.log
|
||||
enocean_2026-03-04_15h30.log
|
||||
```
|
||||
|
||||
### Niveaux de log
|
||||
@@ -210,7 +210,7 @@ enocean_2026-03-04_14h30.log
|
||||
|
||||
```
|
||||
[2026-03-04 00:17:43] [INFO] === EnoceanCLI demarre - Action: Write ===
|
||||
[2026-03-04 00:17:43] [INFO] [MON-AUTOMATE-01] https://10.60.105.42/api/rest/v1 (user: admin)
|
||||
[2026-03-04 00:17:43] [INFO] [MON-AUTOMATE-01] https://192.168.1.11/api/rest/v1 (user: admin)
|
||||
[2026-03-04 00:17:43] [INFO] [MON-AUTOMATE-01] 3 device(s) existant(s) sur l'automate
|
||||
[2026-03-04 00:17:43] [INFO] [MON-AUTOMATE-01] Device enoceandevice1.xml : 12345678 -> 99864513
|
||||
[2026-03-04 00:17:43] [INFO] [MON-AUTOMATE-01] Device enoceandevice2.xml : 87654321 -> 65313272
|
||||
@@ -260,7 +260,7 @@ L'automate n'a pas de capteurs EnOcean configurés. L'action Write ne peut pas m
|
||||
|
||||
### Timeout ou erreur de connexion
|
||||
|
||||
- Vérifier que l'automate est joignable (`ping 10.60.x.x`)
|
||||
- Vérifier que l'automate est joignable (`ping 192.168.1.x`)
|
||||
- Vérifier les identifiants (Username / Password)
|
||||
- Vérifier que le port est correct (HTTP 80 ou HTTPS 443)
|
||||
|
||||
|
||||
@@ -65,7 +65,9 @@ function Invoke-EnoceanGet {
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[AllowEmptyString()]
|
||||
[string]$Password
|
||||
[string]$Password,
|
||||
|
||||
[switch]$RawBytes
|
||||
)
|
||||
|
||||
$headers = Get-AuthHeader -Username $Username -Password $Password
|
||||
@@ -75,6 +77,14 @@ function Invoke-EnoceanGet {
|
||||
|
||||
$response = Invoke-WebRequest -Uri $url -Method GET -Headers $headers -UseBasicParsing -TimeoutSec 30
|
||||
|
||||
# Retourner les bytes bruts si demande (pour fichiers binaires comme .gfx)
|
||||
if ($RawBytes) {
|
||||
if ($response.Content -is [byte[]]) {
|
||||
return , $response.Content
|
||||
}
|
||||
return , [System.Text.Encoding]::UTF8.GetBytes($response.Content)
|
||||
}
|
||||
|
||||
# Si la reponse est un byte[] (encode=bin), decoder en string UTF-8 sans BOM
|
||||
if ($response.Content -is [byte[]]) {
|
||||
return [System.Text.Encoding]::UTF8.GetString($response.Content).TrimStart([char]0xFEFF)
|
||||
@@ -82,7 +92,7 @@ function Invoke-EnoceanGet {
|
||||
return $response.Content
|
||||
}
|
||||
|
||||
function Send-EnoceanConfig {
|
||||
function Send-MultipartFile {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -92,10 +102,13 @@ function Send-EnoceanConfig {
|
||||
[string]$ApiBasePath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[byte[]]$ZipBytes,
|
||||
[string]$ResourcePath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ZipFilename,
|
||||
[byte[]]$FileBytes,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Filename,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Username,
|
||||
@@ -106,17 +119,15 @@ function Send-EnoceanConfig {
|
||||
)
|
||||
|
||||
$authHeaders = Get-AuthHeader -Username $Username -Password $Password
|
||||
$url = "$BaseUrl$ApiBasePath/files/bacnet/inputConfiguration"
|
||||
$url = "$BaseUrl$ApiBasePath/$($ResourcePath.TrimStart('/'))"
|
||||
|
||||
Write-Log -Message "POST $url (fichier: $ZipFilename, taille: $($ZipBytes.Length) octets)" -Level INFO
|
||||
Write-Log -Message "POST $url (fichier: $Filename, taille: $($FileBytes.Length) octets)" -Level INFO
|
||||
|
||||
# Construction multipart manuelle
|
||||
$boundary = [System.Guid]::NewGuid().ToString("N")
|
||||
$encoding = [System.Text.Encoding]::ASCII
|
||||
|
||||
# Partie avant le fichier
|
||||
$headerPart = "--$boundary`r`nContent-Disposition: form-data; name=`"File`"; filename=`"$ZipFilename`"`r`nContent-Type: application/octet-stream`r`n`r`n"
|
||||
# Partie finale
|
||||
$headerPart = "--$boundary`r`nContent-Disposition: form-data; name=`"File`"; filename=`"$Filename`"`r`nContent-Type: application/octet-stream`r`n`r`n"
|
||||
$footerPart = "`r`n--$boundary--`r`n"
|
||||
|
||||
$headerBytes = $encoding.GetBytes($headerPart)
|
||||
@@ -125,7 +136,7 @@ function Send-EnoceanConfig {
|
||||
# Assembler le body complet en byte[]
|
||||
$bodyStream = New-Object System.IO.MemoryStream
|
||||
$bodyStream.Write($headerBytes, 0, $headerBytes.Length)
|
||||
$bodyStream.Write($ZipBytes, 0, $ZipBytes.Length)
|
||||
$bodyStream.Write($FileBytes, 0, $FileBytes.Length)
|
||||
$bodyStream.Write($footerBytes, 0, $footerBytes.Length)
|
||||
$bodyBytes = $bodyStream.ToArray()
|
||||
$bodyStream.Close()
|
||||
@@ -165,4 +176,37 @@ function Send-EnoceanConfig {
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Initialize-ApiClient, Get-AuthHeader, Invoke-EnoceanGet, Send-EnoceanConfig
|
||||
function Send-EnoceanConfig {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$BaseUrl,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ApiBasePath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[byte[]]$ZipBytes,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ZipFilename,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Username,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[AllowEmptyString()]
|
||||
[string]$Password
|
||||
)
|
||||
|
||||
Send-MultipartFile `
|
||||
-BaseUrl $BaseUrl `
|
||||
-ApiBasePath $ApiBasePath `
|
||||
-ResourcePath "files/bacnet/inputConfiguration" `
|
||||
-FileBytes $ZipBytes `
|
||||
-Filename $ZipFilename `
|
||||
-Username $Username `
|
||||
-Password $Password
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Initialize-ApiClient, Get-AuthHeader, Invoke-EnoceanGet, Send-MultipartFile, Send-EnoceanConfig
|
||||
|
||||
@@ -97,4 +97,34 @@ function Update-EnoceanDeviceId {
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-DeviceListFromJson, Parse-EnoceanDeviceXml, New-EnoceanDeviceXml, Update-EnoceanDeviceId
|
||||
function Update-GfxDeviceIds {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$MainXmlContent,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[hashtable]$DeviceIdMap # @{ "ancien_id" = "nouveau_id" }
|
||||
)
|
||||
|
||||
$modifiedXml = $MainXmlContent
|
||||
$replaceCount = 0
|
||||
|
||||
foreach ($oldId in $DeviceIdMap.Keys) {
|
||||
$newId = $DeviceIdMap[$oldId]
|
||||
$pattern = "<DeviceId>$oldId</DeviceId>"
|
||||
$replacement = "<DeviceId>$newId</DeviceId>"
|
||||
|
||||
if ($modifiedXml -match [regex]::Escape($pattern)) {
|
||||
$modifiedXml = $modifiedXml -replace [regex]::Escape($pattern), $replacement
|
||||
$replaceCount++
|
||||
}
|
||||
}
|
||||
|
||||
return @{
|
||||
ModifiedXml = $modifiedXml
|
||||
ReplaceCount = $replaceCount
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-DeviceListFromJson, Parse-EnoceanDeviceXml, New-EnoceanDeviceXml, Update-EnoceanDeviceId, Update-GfxDeviceIds
|
||||
|
||||
@@ -52,4 +52,74 @@ function Get-ZipFilename {
|
||||
return "fullConfig.$hashString.zip"
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function New-EnoceanZip, Get-ZipFilename
|
||||
function Read-GfxMainXml {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[byte[]]$GfxBytes
|
||||
)
|
||||
|
||||
$memStream = New-Object System.IO.MemoryStream(, $GfxBytes)
|
||||
$archive = New-Object System.IO.Compression.ZipArchive($memStream, [System.IO.Compression.ZipArchiveMode]::Read)
|
||||
|
||||
$entry = $archive.GetEntry("Main.xml")
|
||||
if (-not $entry) {
|
||||
$archive.Dispose()
|
||||
$memStream.Close()
|
||||
throw "Main.xml non trouve dans le fichier GFx"
|
||||
}
|
||||
|
||||
$entryStream = $entry.Open()
|
||||
$reader = New-Object System.IO.StreamReader($entryStream, [System.Text.Encoding]::UTF8)
|
||||
$content = $reader.ReadToEnd()
|
||||
$reader.Close()
|
||||
$entryStream.Close()
|
||||
$archive.Dispose()
|
||||
$memStream.Close()
|
||||
|
||||
return $content
|
||||
}
|
||||
|
||||
function Update-GfxZip {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[byte[]]$GfxBytes,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ModifiedMainXml
|
||||
)
|
||||
|
||||
# Copier les bytes dans un MemoryStream modifiable
|
||||
$memStream = New-Object System.IO.MemoryStream
|
||||
$memStream.Write($GfxBytes, 0, $GfxBytes.Length)
|
||||
$memStream.Position = 0
|
||||
|
||||
$archive = New-Object System.IO.Compression.ZipArchive($memStream, [System.IO.Compression.ZipArchiveMode]::Update, $true)
|
||||
|
||||
# Supprimer l'ancien Main.xml et recreer avec le contenu modifie
|
||||
$entry = $archive.GetEntry("Main.xml")
|
||||
if ($entry) {
|
||||
$entry.Delete()
|
||||
}
|
||||
|
||||
$newEntry = $archive.CreateEntry("Main.xml", [System.IO.Compression.CompressionLevel]::Optimal)
|
||||
$entryStream = $newEntry.Open()
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
$writer = New-Object System.IO.StreamWriter($entryStream, $utf8NoBom)
|
||||
$writer.Write($ModifiedMainXml)
|
||||
$writer.Flush()
|
||||
$writer.Close()
|
||||
$entryStream.Close()
|
||||
|
||||
$archive.Dispose()
|
||||
|
||||
$newBytes = $memStream.ToArray()
|
||||
$memStream.Close()
|
||||
|
||||
Write-Log -Message "GFx mis a jour en memoire : $($newBytes.Length) octets" -Level INFO
|
||||
|
||||
return $newBytes
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function New-EnoceanZip, Get-ZipFilename, Read-GfxMainXml, Update-GfxZip
|
||||
|
||||
Reference in New Issue
Block a user