README.md — Documentation complète du projet
Refonte action Write : modifier les XML existants au lieu de les regenerer
This commit is contained in:
165
EnoceanCLI.ps1
165
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
|
||||
}
|
||||
|
||||
$deviceId = $automate.$deviceIdCol
|
||||
$deviceType = $automate.$deviceTypeCol
|
||||
|
||||
# Ignorer si vide
|
||||
if (-not $deviceId -or $deviceId -eq "" -or -not $deviceType -or $deviceType -eq "") {
|
||||
$resourceIndex++
|
||||
if ($deviceFiles.Count -eq 0) {
|
||||
Write-Log -Message "[$hostname] Aucun device Enocean existant sur l'automate - ignore" -Level WARN
|
||||
continue
|
||||
}
|
||||
|
||||
$xmlContent = New-EnoceanDeviceXml `
|
||||
-ResourceNumber $resourceIndex `
|
||||
-DeviceId $deviceId `
|
||||
-DeviceType $deviceType
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
# Trier par ResourceNumber
|
||||
$existingDevices = @($existingDevices | Sort-Object { $_.ResourceNumber })
|
||||
|
||||
# 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
|
||||
}
|
||||
|
||||
$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 `
|
||||
|
||||
269
README.md
Normal file
269
README.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# EnoceanCLI
|
||||
|
||||
Outil en ligne de commande pour lire et écrire les configurations EnOcean sur les automates **Distech Controls Eclypse Gen 1** via leur API REST.
|
||||
|
||||
## Ce que fait cet outil
|
||||
|
||||
- **Read** : Se connecte à chaque automate listé dans un CSV, récupère la configuration EnOcean (DeviceId, DeviceType de chaque capteur), et génère un fichier CSV de sortie.
|
||||
- **Write** : Prend un CSV contenant les DeviceId souhaités et les applique sur chaque automate. **Seul le DeviceId est modifié** — toute la configuration existante sur l'automate (Points, MaxReceiveTime, Description, etc.) est préservée.
|
||||
|
||||
---
|
||||
|
||||
## Prérequis
|
||||
|
||||
- **Windows 10 ou 11** (PowerShell 5.1 est inclus par défaut, rien à installer)
|
||||
- Un accès réseau aux automates Eclypse (HTTP ou HTTPS)
|
||||
- Les identifiants de connexion aux automates (par défaut : `admin` / mot de passe vide)
|
||||
|
||||
### Vérifier que PowerShell est disponible
|
||||
|
||||
Ouvrir un terminal (touche `Windows` + `R`, taper `powershell`, puis `Entrée`) et taper :
|
||||
|
||||
```powershell
|
||||
$PSVersionTable.PSVersion
|
||||
```
|
||||
|
||||
Le numéro `Major` doit être **5 ou plus**.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Aucune installation requise. Télécharger ou cloner le dossier du projet :
|
||||
|
||||
```
|
||||
gfxEnocean/
|
||||
├── EnoceanCLI.ps1 <- Script principal
|
||||
├── modules/ <- Modules (ne pas modifier)
|
||||
│ ├── Logger.psm1
|
||||
│ ├── CsvHandler.psm1
|
||||
│ ├── ApiClient.psm1
|
||||
│ ├── XmlParser.psm1
|
||||
│ └── ZipBuilder.psm1
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Ouvrir PowerShell dans le bon dossier
|
||||
|
||||
1. Ouvrir l'explorateur de fichiers et naviguer dans le dossier `gfxEnocean`
|
||||
2. Cliquer dans la barre d'adresse, taper `powershell`, puis appuyer sur `Entrée`
|
||||
|
||||
Ou bien dans un terminal PowerShell :
|
||||
|
||||
```powershell
|
||||
cd "C:\chemin\vers\gfxEnocean"
|
||||
```
|
||||
|
||||
### Politique d'exécution
|
||||
|
||||
Si c'est la première fois, PowerShell peut bloquer l'exécution des scripts. Autoriser pour la session en cours :
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
|
||||
```
|
||||
|
||||
Cette commande est sans risque : elle n'autorise les scripts que pour la fenêtre PowerShell en cours.
|
||||
|
||||
---
|
||||
|
||||
### Action Read — Lire la configuration des automates
|
||||
|
||||
```powershell
|
||||
.\EnoceanCLI.ps1 -Action Read -CsvInput ".\mon_fichier.csv"
|
||||
```
|
||||
|
||||
Avec un mot de passe :
|
||||
|
||||
```powershell
|
||||
.\EnoceanCLI.ps1 -Action Read -CsvInput ".\mon_fichier.csv" -Password "MonMotDePasse"
|
||||
```
|
||||
|
||||
**Résultat** : Un fichier `enocean_YYYY-MM-DD_HHhMM.csv` est créé dans le dossier courant avec les DeviceId et DeviceType de chaque capteur EnOcean.
|
||||
|
||||
---
|
||||
|
||||
### Action Write — Écrire les DeviceId sur les automates
|
||||
|
||||
```powershell
|
||||
.\EnoceanCLI.ps1 -Action Write -CsvInput ".\enocean_2026-03-03_23h07.csv" -Password "MonMotDePasse"
|
||||
```
|
||||
|
||||
**Ce qui se passe** :
|
||||
1. Le script se connecte à chaque automate
|
||||
2. Il récupère la configuration EnOcean existante (XML complet avec Points, etc.)
|
||||
3. Il remplace **uniquement le DeviceId** par la valeur du CSV
|
||||
4. Il renvoie la configuration modifiée à l'automate
|
||||
|
||||
**Ce qui est préservé** : Points, MaxReceiveTime, Description, Name, ResourceNumber, DeviceType — tout sauf le DeviceId.
|
||||
|
||||
---
|
||||
|
||||
### Aide intégrée
|
||||
|
||||
```powershell
|
||||
Get-Help .\EnoceanCLI.ps1 -Detailed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Format du CSV d'entrée
|
||||
|
||||
Le fichier CSV utilise le **point-virgule** (`;`) comme séparateur.
|
||||
|
||||
### Colonnes obligatoires
|
||||
|
||||
| Colonne | Description |
|
||||
|---------|-------------|
|
||||
| `Hostname` | Nom de l'automate |
|
||||
| `Current Ip` | Adresse IP de l'automate |
|
||||
| `HttpPort` | Port HTTP (`80` ou `-1` si désactivé) |
|
||||
| `HttpsPort` | Port HTTPS (`443` ou `-1` si désactivé) |
|
||||
|
||||
### Colonnes optionnelles
|
||||
|
||||
| Colonne | Description |
|
||||
|---------|-------------|
|
||||
| `RestServiceURL` | Chemin API (défaut : `/api/rest/v1/`) |
|
||||
| `Username` | Login spécifique à cet automate |
|
||||
| `Password` | Mot de passe spécifique à cet automate |
|
||||
|
||||
### Colonnes pour l'action Write
|
||||
|
||||
| Colonne | Description |
|
||||
|---------|-------------|
|
||||
| `DeviceId_1` | DeviceId à écrire sur le 1er capteur EnOcean |
|
||||
| `DeviceType_1` | DeviceType du 1er capteur (pour vérification) |
|
||||
| `DeviceId_2` | DeviceId à écrire sur le 2e capteur |
|
||||
| `DeviceType_2` | DeviceType du 2e capteur |
|
||||
| ... | Autant de paires que de capteurs |
|
||||
|
||||
### Exemple de CSV
|
||||
|
||||
```csv
|
||||
"Hostname";"Current Ip";"HttpPort";"HttpsPort";"RestServiceURL";"DeviceId_1";"DeviceType_1";"DeviceId_2";"DeviceType_2"
|
||||
"MON-AUTOMATE-01";"192.168.1.11";"-1";"443";"/api/rest/v1/";"99864513";"A50401";"65313272";"A5100C"
|
||||
"MON-AUTOMATE-02";"192.168.1.12";"80";"-1";"/api/rest/v1/";"45678912";"A53001";"";""
|
||||
```
|
||||
|
||||
**Note** : Les colonnes DeviceId/DeviceType vides sont ignorées — le capteur correspondant n'est pas modifié.
|
||||
|
||||
---
|
||||
|
||||
## Workflow typique
|
||||
|
||||
### 1. Préparer le CSV d'entrée
|
||||
|
||||
Partir d'un export existant (XNU Export, inventaire réseau...) contenant au minimum les colonnes `Hostname`, `Current Ip`, `HttpPort`, `HttpsPort`.
|
||||
|
||||
### 2. Lire la configuration actuelle
|
||||
|
||||
```powershell
|
||||
.\EnoceanCLI.ps1 -Action Read -CsvInput ".\automates.csv" -Password "MonMotDePasse"
|
||||
```
|
||||
|
||||
Cela génère un CSV avec les DeviceId actuels de chaque automate.
|
||||
|
||||
### 3. Modifier les DeviceId dans le CSV
|
||||
|
||||
Ouvrir le CSV généré dans Excel ou un éditeur de texte. Modifier les colonnes `DeviceId_1`, `DeviceId_2`, etc. avec les nouvelles valeurs.
|
||||
|
||||
**Attention** : Ne pas modifier les colonnes `DeviceType_N`. Elles servent uniquement de référence. Si le DeviceType du CSV diffère de celui de l'automate, un avertissement sera affiché dans les logs.
|
||||
|
||||
### 4. Écrire la nouvelle configuration
|
||||
|
||||
```powershell
|
||||
.\EnoceanCLI.ps1 -Action Write -CsvInput ".\enocean_modifie.csv" -Password "MonMotDePasse"
|
||||
```
|
||||
|
||||
### 5. Vérifier
|
||||
|
||||
Relancer un Read pour confirmer que les DeviceId ont bien changé :
|
||||
|
||||
```powershell
|
||||
.\EnoceanCLI.ps1 -Action Read -CsvInput ".\automates.csv" -Password "MonMotDePasse"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logs
|
||||
|
||||
Chaque exécution génère un fichier log dans le dossier courant :
|
||||
|
||||
```
|
||||
enocean_2026-03-04_14h30.log
|
||||
```
|
||||
|
||||
### Niveaux de log
|
||||
|
||||
| Niveau | Couleur console | Signification |
|
||||
|--------|----------------|---------------|
|
||||
| `INFO` | Cyan | Opération normale |
|
||||
| `SUCCESS` | Vert | Opération réussie |
|
||||
| `WARN` | Jaune | Avertissement (DeviceType différent, colonne manquante...) |
|
||||
| `ERROR` | Rouge | Erreur (connexion échouée, automate injoignable...) |
|
||||
|
||||
### Exemple de log (action Write)
|
||||
|
||||
```
|
||||
[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] 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
|
||||
[2026-03-04 00:17:44] [WARN] [MON-AUTOMATE-01] Device enoceandevice3.xml : DeviceType CSV (D50001) differe du XML (A50401)
|
||||
[2026-03-04 00:17:44] [INFO] [MON-AUTOMATE-01] Device enoceandevice3.xml : 11112222 -> 32602064
|
||||
[2026-03-04 00:17:44] [SUCCESS] [MON-AUTOMATE-01] Configuration envoyee avec succes
|
||||
[2026-03-04 00:17:44] [INFO] ========== RESUME ==========
|
||||
[2026-03-04 00:17:44] [INFO] Automates traites : 1
|
||||
[2026-03-04 00:17:44] [INFO] Automates en erreur : 0
|
||||
[2026-03-04 00:17:44] [INFO] Devices traites : 3
|
||||
[2026-03-04 00:17:44] [INFO] Duree totale : 00:00:01.23
|
||||
[2026-03-04 00:17:44] [INFO] ============================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Résumé des paramètres
|
||||
|
||||
```
|
||||
.\EnoceanCLI.ps1 -Action <Read|Write> -CsvInput <chemin> [-Username <login>] [-Password <mdp>]
|
||||
```
|
||||
|
||||
| Paramètre | Obligatoire | Défaut | Description |
|
||||
|-----------|:-----------:|--------|-------------|
|
||||
| `-Action` | Oui | — | `Read` ou `Write` |
|
||||
| `-CsvInput` | Oui | — | Chemin du fichier CSV d'entrée |
|
||||
| `-Username` | Non | `admin` | Login API (surchargeable par le CSV) |
|
||||
| `-Password` | Non | *(vide)* | Mot de passe API (surchargeable par le CSV) |
|
||||
|
||||
---
|
||||
|
||||
## Dépannage
|
||||
|
||||
### "Impossible d'exécuter le script car l'exécution de scripts est désactivée"
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
|
||||
```
|
||||
|
||||
### "Aucun port HTTP/HTTPS valide"
|
||||
|
||||
Vérifier que les colonnes `HttpPort` et `HttpsPort` du CSV contiennent des valeurs valides. Un port à `-1` signifie "désactivé". Au moins un des deux doit être actif.
|
||||
|
||||
### "Aucun device Enocean existant sur l'automate"
|
||||
|
||||
L'automate n'a pas de capteurs EnOcean configurés. L'action Write ne peut pas modifier des devices qui n'existent pas encore — ils doivent d'abord être créés via le logiciel EC-gfxProgram.
|
||||
|
||||
### Timeout ou erreur de connexion
|
||||
|
||||
- Vérifier que l'automate est joignable (`ping 10.60.x.x`)
|
||||
- Vérifier les identifiants (Username / Password)
|
||||
- Vérifier que le port est correct (HTTP 80 ou HTTPS 443)
|
||||
|
||||
### Le WARN "DeviceType CSV diffère du XML"
|
||||
|
||||
C'est un avertissement informatif. Le DeviceId sera quand même appliqué. Ce message signifie que le type de capteur indiqué dans le CSV ne correspond pas à celui configuré sur l'automate. Vérifier que le bon DeviceId est associé au bon capteur.
|
||||
@@ -75,9 +75,9 @@ function Invoke-EnoceanGet {
|
||||
|
||||
$response = Invoke-WebRequest -Uri $url -Method GET -Headers $headers -UseBasicParsing -TimeoutSec 30
|
||||
|
||||
# Si la reponse est un byte[] (encode=bin), decoder en string UTF-8
|
||||
# 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)
|
||||
return [System.Text.Encoding]::UTF8.GetString($response.Content).TrimStart([char]0xFEFF)
|
||||
}
|
||||
return $response.Content
|
||||
}
|
||||
@@ -105,28 +105,21 @@ function Send-EnoceanConfig {
|
||||
[string]$Password
|
||||
)
|
||||
|
||||
$headers = Get-AuthHeader -Username $Username -Password $Password
|
||||
$authHeaders = Get-AuthHeader -Username $Username -Password $Password
|
||||
$url = "$BaseUrl$ApiBasePath/files/bacnet/inputConfiguration"
|
||||
|
||||
Write-Log -Message "POST $url (fichier: $ZipFilename, taille: $($ZipBytes.Length) octets)" -Level INFO
|
||||
|
||||
# Construction multipart manuelle (compatible PS 5.1, pas de -Form)
|
||||
# Construction multipart manuelle
|
||||
$boundary = [System.Guid]::NewGuid().ToString("N")
|
||||
$headers["Content-Type"] = "multipart/form-data; boundary=$boundary"
|
||||
|
||||
$encoding = [System.Text.Encoding]::ASCII
|
||||
|
||||
# Partie avant le fichier
|
||||
$headerPart = @"
|
||||
--$boundary
|
||||
Content-Disposition: form-data; name="File"; filename="$ZipFilename"
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
"@
|
||||
$headerPart = "--$boundary`r`nContent-Disposition: form-data; name=`"File`"; filename=`"$ZipFilename`"`r`nContent-Type: application/octet-stream`r`n`r`n"
|
||||
# Partie finale
|
||||
$footerPart = "`r`n--$boundary--`r`n"
|
||||
|
||||
$headerBytes = $encoding.GetBytes($headerPart.Replace("`n", "`r`n"))
|
||||
$headerBytes = $encoding.GetBytes($headerPart)
|
||||
$footerBytes = $encoding.GetBytes($footerPart)
|
||||
|
||||
# Assembler le body complet en byte[]
|
||||
@@ -137,10 +130,39 @@ Content-Type: application/octet-stream
|
||||
$bodyBytes = $bodyStream.ToArray()
|
||||
$bodyStream.Close()
|
||||
|
||||
$response = Invoke-WebRequest -Uri $url -Method POST -Headers $headers -Body $bodyBytes -UseBasicParsing -TimeoutSec 60
|
||||
# Utiliser HttpWebRequest directement pour envoyer les bytes bruts sans troncature
|
||||
$request = [System.Net.HttpWebRequest]::Create($url)
|
||||
$request.Method = "POST"
|
||||
$request.ContentType = "multipart/form-data; boundary=$boundary"
|
||||
$request.ContentLength = $bodyBytes.Length
|
||||
$request.Timeout = 60000
|
||||
$request.UserAgent = $authHeaders["User-Agent"]
|
||||
$request.Accept = $authHeaders["Accept"]
|
||||
$request.Headers.Add("Authorization", $authHeaders["Authorization"])
|
||||
|
||||
Write-Log -Message "POST reponse : $($response.StatusCode)" -Level SUCCESS
|
||||
return $response
|
||||
try {
|
||||
$reqStream = $request.GetRequestStream()
|
||||
$reqStream.Write($bodyBytes, 0, $bodyBytes.Length)
|
||||
$reqStream.Close()
|
||||
|
||||
$response = $request.GetResponse()
|
||||
$statusCode = [int]$response.StatusCode
|
||||
$response.Close()
|
||||
|
||||
Write-Log -Message "POST reponse : $statusCode" -Level SUCCESS
|
||||
}
|
||||
catch [System.Net.WebException] {
|
||||
$responseBody = ""
|
||||
if ($_.Exception.Response) {
|
||||
$stream = $_.Exception.Response.GetResponseStream()
|
||||
$reader = New-Object System.IO.StreamReader($stream)
|
||||
$responseBody = $reader.ReadToEnd()
|
||||
$reader.Close()
|
||||
$stream.Close()
|
||||
}
|
||||
Write-Log -Message "POST erreur $($_.Exception.Message) - Reponse serveur: $responseBody" -Level ERROR
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Initialize-ApiClient, Get-AuthHeader, Invoke-EnoceanGet, Send-EnoceanConfig
|
||||
|
||||
@@ -71,4 +71,30 @@ function New-EnoceanDeviceXml {
|
||||
return $xml
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-DeviceListFromJson, Parse-EnoceanDeviceXml, New-EnoceanDeviceXml
|
||||
function Update-EnoceanDeviceId {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$XmlContent,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$NewDeviceId
|
||||
)
|
||||
|
||||
# Parser pour extraire les valeurs actuelles
|
||||
[xml]$xml = $XmlContent
|
||||
$config = $xml.EnOceanDevice.Configuration
|
||||
$oldDeviceId = $config.DeviceId
|
||||
$deviceType = $config.DeviceType
|
||||
|
||||
# Remplacement par regex sur le XML brut (preserve l'encodage et le formatage original)
|
||||
$modifiedXml = $XmlContent -replace "<DeviceId>[^<]*</DeviceId>", "<DeviceId>$NewDeviceId</DeviceId>"
|
||||
|
||||
return @{
|
||||
ModifiedXml = $modifiedXml
|
||||
OldDeviceId = $oldDeviceId
|
||||
DeviceType = $deviceType
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-DeviceListFromJson, Parse-EnoceanDeviceXml, New-EnoceanDeviceXml, Update-EnoceanDeviceId
|
||||
|
||||
@@ -20,7 +20,8 @@ function New-EnoceanZip {
|
||||
$entry = $archive.CreateEntry($entryPath, [System.IO.Compression.CompressionLevel]::Optimal)
|
||||
|
||||
$entryStream = $entry.Open()
|
||||
$writer = New-Object System.IO.StreamWriter($entryStream, [System.Text.Encoding]::UTF8)
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
$writer = New-Object System.IO.StreamWriter($entryStream, $utf8NoBom)
|
||||
$writer.Write($xmlFile.Content)
|
||||
$writer.Flush()
|
||||
$writer.Close()
|
||||
|
||||
Reference in New Issue
Block a user