README.md — Documentation complète du projet
Refonte action Write : modifier les XML existants au lieu de les regenerer
This commit is contained in:
159
EnoceanCLI.ps1
159
EnoceanCLI.ps1
@@ -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 `
|
||||
|
||||
Reference in New Issue
Block a user