Release Version 1
This commit is contained in:
parent
da60ec82de
commit
2d40c40166
BIN
.assets/prtg_channelview.png
Normal file
BIN
.assets/prtg_channelview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 135 KiB |
BIN
.assets/prtg_sensorsettings.png
Normal file
BIN
.assets/prtg_sensorsettings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
57
README.md
57
README.md
@ -1,9 +1,60 @@
|
|||||||
# template_repository
|
# PRTG Sensor: Netgear LM1200 – Datenvolumen & Funkwerte
|
||||||
|
|
||||||
|
Ein PRTG **Programm/Script (Erweitert)** Sensor, der sich am **Netgear LM1200** anmeldet, den monatlichen Datenverbrauch sowie Funkkennzahlen abfragt und als Kanäle an PRTG zurückgibt.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Monatswerte: Limit, Verfuegbar, Verbrauch (GiB) und Verbrauch in Prozent
|
||||||
|
- Sitzung: Empfangene, Gesendete und Gesamt-Daten (MiB)
|
||||||
|
- Funkwerte: RSRP (Signalpegel), RSRQ (Qualitaet), SINR (Stoerabstand)
|
||||||
|
- Sensortext mit IP, Funktyp und Band sowie Anbieter
|
||||||
|
- Stabile Kanalreihenfolge durch fuehrende Nummern (01…11)
|
||||||
|
|
||||||
Wichtig: Link für Lizenz anpassen.
|
## Ablauf PRTG (Installation)
|
||||||
|
1. Datei ablegen unter:
|
||||||
|
`C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\lm1200-prtg-usage.v1.ps1`
|
||||||
|
2. In PRTG einen neuen Sensor anlegen:
|
||||||
|
Sensor-Typ: `Programm/Script (Erweitert)`
|
||||||
|
In den Spezifischen Sensoreinstellungen das Script `lm1200-prtg-usage.v1.ps1` auswaehlen.
|
||||||
|
3. Parameter eintragen (nur diese beiden sind noetig):
|
||||||
|
`-LmIp IP_ADRESSE -LmPass MEIN_PASSWORD`
|
||||||
|
|
||||||
|
Beispiel:
|
||||||
|
`-LmIp 192.168.178.1 -LmPass MeinSuperPasswort`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Getestete Umgebung
|
||||||
|
- Hardware: Netgear LM1200
|
||||||
|
- PRTG: 25.3.110.1313
|
||||||
|
|
||||||
|
## Ausgegebenen Kanaele (Beispiel)
|
||||||
|
01 Datenvolumen Limit (GiB)
|
||||||
|
02 Datenvolumen Verfuegbar (GiB)
|
||||||
|
03 Datenvolumen Verbrauch aktueller Monat (GiB)
|
||||||
|
04 Datenvolumen Verbrauch aktueller Monat (%)
|
||||||
|
05 Tage bis Abrechnungszyklus
|
||||||
|
06 Empfangene Daten (Sitzung, MiB)
|
||||||
|
07 Gesendete Daten (Sitzung, MiB)
|
||||||
|
08 Daten gesamt (Sitzung, MiB)
|
||||||
|
09 RSRP (Signalpegel)
|
||||||
|
10 RSRQ (Qualitaet)
|
||||||
|
11 SINR (Stoerabstand)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Hinweise
|
||||||
|
- Das Script nutzt den LM1200-Login-Flow (secToken + Session) und liest api/model.json.
|
||||||
|
- Einheiten im Namen: GiB/MiB (binary). Alarme komfortabel ueber Channel-Limits in PRTG setzen (z. B. Kanal 02: Warning 5 GiB, Error 2 GiB).
|
||||||
|
- Wenn Kanalnamen geaendert werden, am einfachsten den Sensor einmal loeschen und neu anlegen, damit keine Alt-Kanaele bleiben.
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
MIT
|
||||||
|
|
||||||
|
## 💬 Support & Community
|
||||||
|
Du hast Fragen, brauchst Unterstützung bei der Einrichtung oder möchtest dich einfach mit anderen austauschen, die ähnliche Projekte betreiben? Dann schau gerne in unserer Techniverse Community vorbei:
|
||||||
|
|
||||||
|
👉 **Matrix-Raum:** [#community:techniverse.net](https://matrix.to/#/#community:techniverse.net)
|
||||||
|
Wir freuen uns auf deinen Besuch und helfen dir gerne weiter!
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -11,5 +62,5 @@ Wichtig: Link für Lizenz anpassen.
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://assets.techniverse.net/f1/logos/small/license.png" alt="License" width="15" height="15"> <a href="./template_repository/src/branch/main/LICENSE">License</a> | <img src="https://assets.techniverse.net/f1/logos/small/matrix2.svg" alt="Matrix" width="15" height="15"> <a href="https://matrix.to/#/#community:techniverse.net">Matrix</a> | <img src="https://assets.techniverse.net/f1/logos/small/mastodon2.svg" alt="Matrix" width="15" height="15"> <a href="https://social.techniverse.net/@donnerwolke">Mastodon</a>
|
<img src="https://assets.techniverse.net/f1/logos/small/license.png" alt="License" width="15" height="15"> <a href="./lm1200-prtg-datausage-sensor/src/branch/main/LICENSE">License</a> | <img src="https://assets.techniverse.net/f1/logos/small/matrix2.svg" alt="Matrix" width="15" height="15"> <a href="https://matrix.to/#/#community:techniverse.net">Matrix</a> | <img src="https://assets.techniverse.net/f1/logos/small/mastodon2.svg" alt="Matrix" width="15" height="15"> <a href="https://social.techniverse.net/@donnerwolke">Mastodon</a>
|
||||||
</p>
|
</p>
|
153
lm1200-prtg-usage.v1.ps1
Normal file
153
lm1200-prtg-usage.v1.ps1
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# Script Name: lm1200-prtg-usage.v1.ps1
|
||||||
|
# Beschreibung: Überwacht einen Netgear LM1200 auf Traffic und Netzwerkstatus
|
||||||
|
# Aufruf: powershell -NoProfile -ExecutionPolicy Bypass -File .\lm1200-prtg-usage.v1.ps1 -LmIp IP_ADRESSE -LmPass "MEIN_PASSWORD"
|
||||||
|
# Autor: Patrick Asmus
|
||||||
|
# Web: https://www.cleveradmin.de
|
||||||
|
# Git-Reposit.: https://git.techniverse.net/scriptos/lm1200-prtg-datausage-sensor.git
|
||||||
|
# Version: 1.0
|
||||||
|
# Datum: 23.09.2025
|
||||||
|
# Modifikation: Release Version 1
|
||||||
|
#####################################################
|
||||||
|
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)][string]$LmIp,
|
||||||
|
[ValidateSet("http","https")][string]$LmProto = "http",
|
||||||
|
[Parameter(Mandatory = $true)][string]$LmPass,
|
||||||
|
[int]$TimeoutSec = 10,
|
||||||
|
[switch]$InsecureTls,
|
||||||
|
[double]$MinRemainingGiB = [double]::NaN,
|
||||||
|
[double]$MaxPercentUsed = [double]::NaN,
|
||||||
|
[switch]$TripSensorOnThreshold
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$ProgressPreference = 'SilentlyContinue'
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo]::InvariantCulture
|
||||||
|
[System.Threading.Thread]::CurrentThread.CurrentUICulture = [System.Globalization.CultureInfo]::InvariantCulture
|
||||||
|
|
||||||
|
$Base = "${LmProto}://${LmIp}"
|
||||||
|
$Sess = New-Object Microsoft.PowerShell.Commands.WebRequestSession
|
||||||
|
$Headers = @{ "Accept"="application/json"; "X-Requested-With"="XMLHttpRequest" }
|
||||||
|
$HasBasic = $PSVersionTable.PSVersion.Major -lt 6
|
||||||
|
|
||||||
|
if ($LmProto -eq "https" -and $InsecureTls) {
|
||||||
|
Add-Type @"
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
public class TrustAllCertsPolicy : ICertificatePolicy {
|
||||||
|
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; }
|
||||||
|
}
|
||||||
|
"@ | Out-Null
|
||||||
|
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-Json([string]$Uri) {
|
||||||
|
if ($HasBasic) { Invoke-RestMethod -UseBasicParsing -Uri $Uri -Headers $Headers -WebSession $Sess -TimeoutSec $TimeoutSec -MaximumRedirection 5 }
|
||||||
|
else { Invoke-RestMethod -Uri $Uri -Headers $Headers -WebSession $Sess -TimeoutSec $TimeoutSec -MaximumRedirection 5 }
|
||||||
|
}
|
||||||
|
function Invoke-Post([string]$Uri, [hashtable]$Form) {
|
||||||
|
if ($HasBasic) { Invoke-WebRequest -UseBasicParsing -Uri $Uri -Method Post -Body $Form -ContentType "application/x-www-form-urlencoded" -Headers @{'X-Requested-With'='XMLHttpRequest'} -WebSession $Sess -TimeoutSec $TimeoutSec | Out-Null }
|
||||||
|
else { Invoke-WebRequest -Uri $Uri -Method Post -Body $Form -ContentType "application/x-www-form-urlencoded" -Headers @{'X-Requested-With'='XMLHttpRequest'} -WebSession $Sess -TimeoutSec $TimeoutSec | Out-Null }
|
||||||
|
}
|
||||||
|
|
||||||
|
function XmlEsc([string]$s){
|
||||||
|
if($null -eq $s){ return "" }
|
||||||
|
($s -replace '&','&') -replace '<','<' -replace '>','>'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Num([object]$x){ if($null -eq $x){[double]0}else{ try{[double]$x}catch{[double]0}} }
|
||||||
|
|
||||||
|
function ChannelXml([string]$name,[double]$value,[string]$unit,[string]$customUnit,[bool]$isFloat){
|
||||||
|
$valStr = $value.ToString([System.Globalization.CultureInfo]::InvariantCulture)
|
||||||
|
$n = XmlEsc $name
|
||||||
|
$cu = if($customUnit){ XmlEsc $customUnit } else { "" }
|
||||||
|
$sb = New-Object System.Text.StringBuilder
|
||||||
|
[void]$sb.Append("<result><channel>$n</channel><value>$valStr</value>")
|
||||||
|
if($unit){ [void]$sb.Append("<unit>$unit</unit>") }
|
||||||
|
if($customUnit){ [void]$sb.Append("<customunit>$cu</customunit>") }
|
||||||
|
if($isFloat){ [void]$sb.Append("<float>1</float><decimalmode>2</decimalmode>") }
|
||||||
|
[void]$sb.Append("</result>")
|
||||||
|
$sb.ToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-PrtgError([string]$msg){ "<prtg><error>1</error><text>$(XmlEsc $msg)</text></prtg>" }
|
||||||
|
function Write-PrtgOk([System.Collections.Generic.List[string]]$results,[string]$text="OK",[bool]$trip=$false,[string]$tripText=""){
|
||||||
|
$sb=New-Object System.Text.StringBuilder
|
||||||
|
[void]$sb.Append("<prtg>")
|
||||||
|
foreach($r in $results){ [void]$sb.Append($r) }
|
||||||
|
$t = if($trip -and $tripText){ $tripText } else { $text }
|
||||||
|
[void]$sb.Append("<text>$(XmlEsc $t)</text>")
|
||||||
|
if($trip){ [void]$sb.Append("<error>1</error>") }
|
||||||
|
[void]$sb.Append("</prtg>")
|
||||||
|
$sb.ToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DaysToBilling([int]$billingDay){
|
||||||
|
if($billingDay -lt 1 -or $billingDay -gt 31){ return [double]::NaN }
|
||||||
|
$now = [DateTime]::Now.Date
|
||||||
|
$daysInThis = [DateTime]::DaysInMonth($now.Year, $now.Month)
|
||||||
|
$targetThis = [DateTime]::new($now.Year, $now.Month, [Math]::Min($billingDay,$daysInThis))
|
||||||
|
if($now -lt $targetThis){ return [double]([int]($targetThis - $now).TotalDays) }
|
||||||
|
$next = $now.AddMonths(1)
|
||||||
|
$daysInNext = [DateTime]::DaysInMonth($next.Year, $next.Month)
|
||||||
|
$targetNext = [DateTime]::new($next.Year, $next.Month, [Math]::Min($billingDay,$daysInNext))
|
||||||
|
return [double]([int]($targetNext - $now).TotalDays)
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
$model1 = Invoke-Json "$Base/api/model.json"
|
||||||
|
$token = $model1.session.secToken
|
||||||
|
if(-not $token){ throw "secToken nicht gefunden (Login-Flow geaendert?)" }
|
||||||
|
|
||||||
|
Invoke-Post "$Base/Forms/config" @{
|
||||||
|
token = $token; err_redirect='/index.html?loginfailed'; ok_redirect='/index.html'; 'session.password'=$LmPass.Trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
$m = Invoke-Json "$Base/api/model.json"
|
||||||
|
|
||||||
|
$GiB=[double][Math]::Pow(2,30); $MiB=[double][Math]::Pow(2,20)
|
||||||
|
$usedBytes = Num $m.wwan.dataUsage.generic.dataTransferred
|
||||||
|
$limitBytes = Num $m.wwan.dataUsage.generic.billingCycleLimit
|
||||||
|
$billDayRaw = [int](Num $m.wwan.dataUsage.generic.billingDay)
|
||||||
|
$limitKnown = $limitBytes -gt 0
|
||||||
|
$remainBytes = if($limitKnown){$limitBytes-$usedBytes}else{[double]::NaN}
|
||||||
|
$usedGiB = [Math]::Round(($usedBytes/$GiB), 2)
|
||||||
|
$limitGiB = if($limitKnown){[Math]::Round(($limitBytes/$GiB), 2)}else{[double]::NaN}
|
||||||
|
$remainGiB = if($limitKnown){[Math]::Round(($remainBytes/$GiB), 2)}else{[double]::NaN}
|
||||||
|
$percentUsed = if($limitKnown){[Math]::Round((($usedBytes*100.0)/$limitBytes), 2)}else{[double]::NaN}
|
||||||
|
$rxMiB=[Math]::Round((Num $m.wwan.dataTransferred.rxb)/$MiB,2)
|
||||||
|
$txMiB=[Math]::Round((Num $m.wwan.dataTransferred.txb)/$MiB,2)
|
||||||
|
$totMiB=[Math]::Round((Num $m.wwan.dataTransferred.totalb)/$MiB,2)
|
||||||
|
$daysToBill = if($limitKnown -and $billDayRaw -gt 0){ Get-DaysToBilling $billDayRaw } else { [double]::NaN }
|
||||||
|
|
||||||
|
$provider = $null
|
||||||
|
try{ $defId = $m.wwan.profile.default; $provider = ($m.wwan.profileList | Where-Object { $_.id -eq $defId } | Select-Object -First 1).name }catch{}
|
||||||
|
if([string]::IsNullOrWhiteSpace($provider)){ $provider = $m.wwan.registerNetworkDisplay }
|
||||||
|
if([string]::IsNullOrWhiteSpace($provider)){ $mcc=$m.wwanadv.MCC; $mnc=$m.wwanadv.MNC; if($mcc -and $mnc){ $provider="$mcc-$mnc" } }
|
||||||
|
$radio = $m.wwan.connectionText
|
||||||
|
$band = $m.wwanadv.curBand
|
||||||
|
$ip = $m.wwan.IP
|
||||||
|
|
||||||
|
$res=New-Object 'System.Collections.Generic.List[string]'
|
||||||
|
function N($i){ $i.ToString().PadLeft(2,'0') }
|
||||||
|
$res.Add( (ChannelXml "$(N 1) Datenvolumen Limit (GiB)" $limitGiB "Custom" "GiB" $true) )
|
||||||
|
$res.Add( (ChannelXml "$(N 2) Datenvolumen Verfuegbar (GiB)" $remainGiB "Custom" "GiB" $true) )
|
||||||
|
$res.Add( (ChannelXml "$(N 3) Datenvolumen Verbrauch aktueller Monat (GiB)" $usedGiB "Custom" "GiB" $true) )
|
||||||
|
$res.Add( (ChannelXml "$(N 4) Datenvolumen Verbrauch aktueller Monat (%)" $percentUsed "Percent" "" $true) )
|
||||||
|
$res.Add( (ChannelXml "$(N 5) Tage bis zum neuen Abrechnungszyklus" $daysToBill "Custom" "Tage" $false) )
|
||||||
|
$res.Add( (ChannelXml "$(N 6) Empfangene Daten (Sitzung, MiB)" $rxMiB "Custom" "MiB" $true) )
|
||||||
|
$res.Add( (ChannelXml "$(N 7) Gesendete Daten (Sitzung, MiB)" $txMiB "Custom" "MiB" $true) )
|
||||||
|
$res.Add( (ChannelXml "$(N 8) Daten gesamt (Sitzung, MiB)" $totMiB "Custom" "MiB" $true) )
|
||||||
|
$res.Add( (ChannelXml "$(N 9) RSRP (Signalpegel)" (Num $m.wwan.signalStrength.rsrp) "Custom" "dBm" $false) )
|
||||||
|
$res.Add( (ChannelXml "$(N 10) RSRQ (Qualitaet)" (Num $m.wwan.signalStrength.rsrq) "Custom" "dB" $false) )
|
||||||
|
$res.Add( (ChannelXml "$(N 11) SINR (Stoerabstand)" (Num $m.wwan.signalStrength.sinr) "Custom" "dB" $false) )
|
||||||
|
|
||||||
|
$title = "IP $ip | Funk $radio | $band | Anbieter $provider"
|
||||||
|
[Console]::Out.Write( (Write-PrtgOk -results $res -text $title -trip ($TripSensorOnThreshold -and $limitKnown -and ((-not [double]::IsNaN($MinRemainingGiB) -and $remainGiB -le $MinRemainingGiB) -or (-not [double]::IsNaN($MaxPercentUsed) -and $percentUsed -ge $MaxPercentUsed)))) )
|
||||||
|
}
|
||||||
|
catch{
|
||||||
|
[Console]::Out.Write( (Write-PrtgError $_.Exception.Message) )
|
||||||
|
exit 1
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user