README.md — Documentation complète du projet

Refonte action Write : modifier les XML existants au lieu de les regenerer
This commit is contained in:
2026-03-04 08:16:23 +01:00
parent c75c731ddc
commit 83df3cc4ef
5 changed files with 467 additions and 46 deletions

View File

@@ -1,5 +1,59 @@
# EnoceanCLI.ps1 - CLI Enocean pour automates Distech Controls Eclypse
# Lecture/ecriture des configurations Enocean via REST API
<#
.SYNOPSIS
CLI Enocean pour automates Distech Controls Eclypse.
Lecture et ecriture des configurations Enocean via REST API.
.DESCRIPTION
EnoceanCLI permet de gerer les configurations EnOcean sur des automates
Distech Controls Eclypse Gen 1 (ECY-xxx) en utilisant leur API REST.
Action READ : Lit la configuration EnOcean de chaque automate et genere
un fichier CSV avec les DeviceId et DeviceType de chaque device.
Action WRITE : Modifie les DeviceId sur chaque automate a partir du CSV.
Seul le DeviceId est modifie, toute la configuration existante
(Points, MaxReceiveTime, Description...) est preservee.
.PARAMETER Action
Action a effectuer :
Read - Lire la configuration et generer un CSV de sortie
Write - Ecrire les DeviceId depuis le CSV vers les automates
.PARAMETER CsvInput
Chemin vers le fichier CSV d'entree (separateur point-virgule).
Le CSV doit contenir au minimum : Hostname, Current Ip, HttpPort, HttpsPort.
Pour l'action Write, il doit aussi contenir les colonnes DeviceId_1, DeviceId_2, etc.
.PARAMETER Username
Nom d'utilisateur pour l'authentification API (defaut: admin).
Peut etre surcharge par la colonne Username du CSV.
.PARAMETER Password
Mot de passe pour l'authentification API (defaut: vide).
Peut etre surcharge par la colonne Password du CSV.
.EXAMPLE
.\EnoceanCLI.ps1 -Action Read -CsvInput ".\automates.csv"
Lit la configuration EnOcean de tous les automates listes dans le CSV.
Genere un fichier enocean_YYYY-MM-DD_HHhMM.csv dans le repertoire courant.
.EXAMPLE
.\EnoceanCLI.ps1 -Action Write -CsvInput ".\enocean_2026-03-03_23h07.csv" -Password "MonMotDePasse"
Ecrit les DeviceId du CSV vers les automates. Les configurations existantes
(Points, MaxReceiveTime, etc.) sont preservees.
.EXAMPLE
Get-Help .\EnoceanCLI.ps1 -Detailed
Affiche cette aide detaillee.
.NOTES
Prerequis : PowerShell 5.1+ (inclus dans Windows 10/11)
API : Distech Controls Eclypse REST API v1
Securite : TLS 1.2, certificats auto-signes acceptes
#>
param(
[Parameter(Mandatory)]
@@ -143,47 +197,96 @@ elseif ($Action -eq "Write") {
Write-Log -Message "[$hostname] $baseUrl$apiBasePath (user: $($creds.Username))" -Level INFO
# Lire les colonnes dynamiques DeviceId_N / DeviceType_N
$xmlFiles = @()
$resourceIndex = 1
# GET liste des devices Enocean existants sur l'automate
$jsonContent = Invoke-EnoceanGet `
-BaseUrl $baseUrl `
-ApiBasePath $apiBasePath `
-ResourcePath "files/enocean/configuration/devices" `
-Username $creds.Username `
-Password $creds.Password
while ($true) {
$deviceIdCol = "DeviceId_$resourceIndex"
$deviceTypeCol = "DeviceType_$resourceIndex"
$deviceFiles = Get-DeviceListFromJson -JsonContent $jsonContent
# Verifier si les colonnes existent
$props = $automate.PSObject.Properties.Name
if ($deviceIdCol -notin $props -or $deviceTypeCol -notin $props) {
break
if ($deviceFiles.Count -eq 0) {
Write-Log -Message "[$hostname] Aucun device Enocean existant sur l'automate - ignore" -Level WARN
continue
}
Write-Log -Message "[$hostname] $($deviceFiles.Count) device(s) existant(s) sur l'automate" -Level INFO
# GET chaque XML existant et parser pour obtenir ResourceNumber
$existingDevices = @()
foreach ($deviceFile in $deviceFiles) {
try {
$xmlContent = Invoke-EnoceanGet `
-BaseUrl $baseUrl `
-ApiBasePath $apiBasePath `
-ResourcePath "files/enocean/configuration/devices/$($deviceFile.Name)?encode=bin" `
-Username $creds.Username `
-Password $creds.Password
$parsed = Parse-EnoceanDeviceXml -XmlContent $xmlContent
$existingDevices += @{
Name = $deviceFile.Name
XmlContent = $xmlContent
ResourceNumber = $parsed.ResourceNumber
DeviceType = $parsed.DeviceType
DeviceId = $parsed.DeviceId
}
}
catch {
Write-Log -Message "[$hostname] Erreur lecture $($deviceFile.Name) : $($_.Exception.Message)" -Level ERROR
}
}
$deviceId = $automate.$deviceIdCol
$deviceType = $automate.$deviceTypeCol
# Trier par ResourceNumber
$existingDevices = @($existingDevices | Sort-Object { $_.ResourceNumber })
# Ignorer si vide
if (-not $deviceId -or $deviceId -eq "" -or -not $deviceType -or $deviceType -eq "") {
$resourceIndex++
# Associer par index sequentiel : device[0] -> DeviceId_1, device[1] -> DeviceId_2, etc.
$xmlFiles = @()
for ($i = 0; $i -lt $existingDevices.Count; $i++) {
$seqIndex = $i + 1
$deviceIdCol = "DeviceId_$seqIndex"
$deviceTypeCol = "DeviceType_$seqIndex"
$device = $existingDevices[$i]
# Verifier si la colonne DeviceId_N existe dans le CSV
$props = $automate.PSObject.Properties.Name
if ($deviceIdCol -notin $props) {
Write-Log -Message "[$hostname] Colonne $deviceIdCol absente du CSV - device $($device.Name) non modifie" -Level WARN
continue
}
$xmlContent = New-EnoceanDeviceXml `
-ResourceNumber $resourceIndex `
-DeviceId $deviceId `
-DeviceType $deviceType
$newDeviceId = $automate.$deviceIdCol
# Ignorer si DeviceId vide
if (-not $newDeviceId -or $newDeviceId -eq "") {
$seqIndex++
continue
}
# Verifier DeviceType CSV vs XML si la colonne existe
if ($deviceTypeCol -in $props) {
$csvDeviceType = $automate.$deviceTypeCol
if ($csvDeviceType -and $csvDeviceType -ne "" -and $csvDeviceType -ne $device.DeviceType) {
Write-Log -Message "[$hostname] Device $($device.Name) : DeviceType CSV ($csvDeviceType) differe du XML ($($device.DeviceType))" -Level WARN
}
}
# Modifier uniquement le DeviceId dans le XML existant
$result = Update-EnoceanDeviceId -XmlContent $device.XmlContent -NewDeviceId $newDeviceId
$xmlFiles += @{
Name = "enoceandevice$resourceIndex.xml"
Content = $xmlContent
Name = $device.Name
Content = $result.ModifiedXml
}
Update-Stats -Counter DevicesProcessed
Write-Log -Message "[$hostname] XML genere : device $resourceIndex (Id=$deviceId, Type=$deviceType)" -Level INFO
$resourceIndex++
Write-Log -Message "[$hostname] Device $($device.Name) : $($result.OldDeviceId) -> $newDeviceId" -Level INFO
}
if ($xmlFiles.Count -eq 0) {
Write-Log -Message "[$hostname] Aucun device a ecrire - ignore" -Level WARN
Write-Log -Message "[$hostname] Aucun device a modifier - ignore" -Level WARN
continue
}
@@ -191,7 +294,7 @@ elseif ($Action -eq "Write") {
$zipBytes = New-EnoceanZip -XmlFiles $xmlFiles
$zipFilename = Get-ZipFilename -ZipBytes $zipBytes
Write-Log -Message "[$hostname] Envoi de $($xmlFiles.Count) device(s) ($zipFilename)..." -Level INFO
Write-Log -Message "[$hostname] Envoi de $($xmlFiles.Count) device(s) modifie(s) ($zipFilename)..." -Level INFO
Send-EnoceanConfig `
-BaseUrl $baseUrl `