Ho fatto scrivere alle AI due script, uno bash per Linux, e l'altro PowerShell per Windows.
Principalmente li ho fatti scrivere a ChatGPT, e poi li ho fatti controllare a Mistral Small 3 e o3-mini che hanno suggerito ulteriori implementazioni.
Io gli ho solo detto quello che volevo che facessero gli script, ma non ci capisco niente di programmazione o scripting, non so come si dice in questi casi 🤪 quindi spero che quelli di voi che se ne intendono possano verificare che siano scritti bene.
Entrambi gli script servono per controllare la batteria della mia tastiera wireless, e notificarmi quando è scarica al 20% e quando si carica fino all'80%, in modo da preservare al meglio la batteria.
Lo script bash viene avviato all'accesso tramite un altro piccolo script che avvia KTime, visto che non sono riuscito a farlo funzionare altrimenti, e si appoggia su Solaar per funzionare, mentre quello di Windows è basato quasi interamente sulle funzioni del sistema, richiede solo l'installazione di BurntToast per visualizzare gli avvisi nel centro notifiche di Windows 10. Avevo provato anche ad installare Logi Option+, ma non funzionava bene da account standard, così l'ho tolto. A differenza dello script per Linux, quello per Windows intendo avviarlo manualmente e non averlo sempre in esecuzione, infetti è scritto per interrompersi da solo dopo 30 minuti che la tastiera è stata spenta, se non lo interrompo io prima, e visto che la collegherei ad un portatile la cosa ha senso, almeno per me 😀
Come ho già detto io non ci capisco niente di queste cose, quindi eventuali miglioramenti o correzzioni li farò comunque fare ad una AI per evitare errori.
Script Bash
#!/bin/bash
# Configurazione
DEVICE_NAME="MX Keys S"
UPPER_THRESHOLD=80
LOWER_THRESHOLD=20
NOTIFY_INTERVAL=600
LOG_FILE="$HOME/logs/mx_keys_battery_monitor.log"
NOTIFY_CHARGE_FULL="Scollega il cavo: la batteria è sufficientemente carica!"
NOTIFY_CHARGE_LOW="La batteria è sotto il 20%! Metti in carica!"
mkdir -p "$(dirname "$LOG_FILE")"
# Controllo preliminare: verifica che il file di log sia scrivibile
if ! touch "$LOG_FILE" &>/dev/null; then
echo "Errore: il file di log $LOG_FILE non è scrivibile. Verifica i permessi."
exit 1
fi
# Variabile per tenere traccia dell'ultimo stato notificato
# Formato: "<livello_batteria>_<stato_carica>"
LAST_NOTIFICATION=""
# Funzione per il logging
log_message() {
local message="$1"
echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" | tee -a "$LOG_FILE"
}
log_message "🔋 Avvio monitoraggio batteria per '$DEVICE_NAME' (soglie: $LOWER_THRESHOLD% e $UPPER_THRESHOLD%)."
while true; do
# Ottieni l'output di solaar
SOLAAR_OUTPUT=$(solaar show 2>/dev/null)
# Verifica che l'output contenga il DEVICE_NAME
if ! echo "$SOLAAR_OUTPUT" | grep -q "$DEVICE_NAME"; then
log_message "⚠️ Dispositivo '$DEVICE_NAME' non trovato o non connesso."
sleep "$NOTIFY_INTERVAL"
continue
fi
# Estrai il blocco informativo relativo al dispositivo
DEVICE_INFO=$(echo "$SOLAAR_OUTPUT" | awk "/$DEVICE_NAME/,/Wireless Receiver/")
# Verifica che il blocco non sia vuoto
if [[ -z "$DEVICE_INFO" ]]; then
log_message "⚠️ Impossibile estrarre le informazioni dal dispositivo '$DEVICE_NAME'."
sleep "$NOTIFY_INTERVAL"
continue
fi
# Estrai il livello di batteria.
# Si assume un formato tipo: "Battery: 82% (Charging)" oppure "Battery: 19% (Discharging)"
BATTERY_LEVEL=$(echo "$DEVICE_INFO" | grep -oP 'Battery:\s*\K\d+(?=%)')
# Estrai lo stato di carica (all'interno delle parentesi)
CHARGE_STATUS=$(echo "$DEVICE_INFO" | grep -oP 'Battery:\s*\d+%\s*\(\K[^)]+' )
# Se non si riesce ad ottenere uno dei due valori, logga l'errore e continua
if [[ -z "$BATTERY_LEVEL" || -z "$CHARGE_STATUS" ]]; then
log_message "⚠️ Impossibile ottenere il livello (% valore: '$BATTERY_LEVEL') o lo stato di carica (valore: '$CHARGE_STATUS')."
sleep "$NOTIFY_INTERVAL"
continue
fi
# Crea uno stato corrente
CURRENT_STATE="${BATTERY_LEVEL}_${CHARGE_STATUS}"
# Azzeramento della variabile LAST_NOTIFICATION se lo stato è cambiato in maniera sostanziale:
# ad esempio se il dispositivo passa da 'Charging' a 'Discharging' pur restando nella stessa fascia di carica.
if [[ "$LAST_NOTIFICATION" != "" ]]; then
# Se il vecchio stato e quello attuale hanno lo stesso livello ma stati diversi
local_last_status="${LAST_NOTIFICATION##*_}"
if [[ "$local_last_status" != "$CHARGE_STATUS" && "$BATTERY_LEVEL" -gt "$LOWER_THRESHOLD" && "$BATTERY_LEVEL" -lt "$UPPER_THRESHOLD" ]]; then
LAST_NOTIFICATION=""
fi
fi
# Notifica se la batteria è al di sopra della soglia e in carica
if [[ "$BATTERY_LEVEL" -ge "$UPPER_THRESHOLD" && "$CHARGE_STATUS" == "Charging" && "$CURRENT_STATE" != "$LAST_NOTIFICATION" ]]; then
notify-send -u critical "🔋 $DEVICE_NAME ($BATTERY_LEVEL%)" "$NOTIFY_CHARGE_FULL"
log_message "Notifica inviata: batteria $BATTERY_LEVEL% - $CHARGE_STATUS. $NOTIFY_CHARGE_FULL"
LAST_NOTIFICATION="$CURRENT_STATE"
fi
# Notifica se la batteria è al di sotto della soglia e non in carica
if [[ "$BATTERY_LEVEL" -le "$LOWER_THRESHOLD" && "$CHARGE_STATUS" != "Charging" && "$CURRENT_STATE" != "$LAST_NOTIFICATION" ]]; then
notify-send -u critical "⚠️ $DEVICE_NAME ($BATTERY_LEVEL%)" "$NOTIFY_CHARGE_LOW"
log_message "Notifica inviata: batteria $BATTERY_LEVEL% - $CHARGE_STATUS. $NOTIFY_CHARGE_LOW"
LAST_NOTIFICATION="$CURRENT_STATE"
fi
# Log dello stato corrente per debug
log_message "Stato corrente: $BATTERY_LEVEL% - $CHARGE_STATUS"
sleep "$NOTIFY_INTERVAL"
done
Script PowerShell
# ===================== CONFIGURAZIONE =====================
$lockFilePath = "$env:TEMP\mxkeys_monitor.lock"
$lowThreshold = 20
$highThreshold = 80
$deviceNamePattern = "*MX Keys*"
$checkIntervalSeconds = 300
$maxMissingChecks = 6 # 30 minuti = 6*5 min
$periodicNotify = $true
$periodicNotifyInterval = 30 # minuti
# ===================== INIZIALIZZAZIONE ====================
Import-Module BurntToast -ErrorAction SilentlyContinue
function Show-ToastNotification {
param ([string]$message)
try {
New-BurntToastNotification -Text "MX Keys S", $message
} catch {
Write-Host "❌ Errore invio notifica: $_"
}
}
# ===================== CONTROLLO LOCK FILE =================
if (Test-Path $lockFilePath) {
try {
$existingPid = Get-Content $lockFilePath
if ($existingPid -match '^\d+$') {
$proc = Get-Process -Id $existingPid -ErrorAction SilentlyContinue
if ($proc) {
Write-Host "Trovata un'altra istanza ($existingPid). La termino..."
$proc.Kill()
Start-Sleep -Seconds 1
}
}
} catch {
Write-Host "Errore gestione lock: $_"
}
}
# Registra nuovo PID
$PID | Out-File -FilePath $lockFilePath -Force
# ===================== MONITORAGGIO ====================
$notifiedLow = $false
$notifiedHigh = $false
$lastBatteryLevel = -1
$lastNotificationTime = Get-Date
$missingCount = 0
try {
while ($true) {
$device = Get-PnpDevice | Where-Object { $_.FriendlyName -like $deviceNamePattern -and $_.Status -eq "OK" }
if ($device) {
$missingCount = 0
$batteryInfo = Get-PnpDeviceProperty -InstanceId $device.InstanceId |
Where-Object { $_.KeyName -like "*Battery*" }
if ($batteryInfo -and $batteryInfo.Data -is [int]) {
$batteryLevel = [int]$batteryInfo.Data
$now = Get-Date
Write-Host "$($now.ToString("HH:mm:ss")) - Batteria: $batteryLevel%"
$batteryChanged = ($batteryLevel -ne $lastBatteryLevel)
$timeSinceLastNotify = ($now - $lastNotificationTime).TotalMinutes
if ($batteryLevel -lt $lowThreshold -and (-not $notifiedLow -or ($periodicNotify -and $timeSinceLastNotify -ge $periodicNotifyInterval))) {
Show-ToastNotification "Batteria sotto il $lowThreshold% — collega la tastiera al caricatore."
$notifiedLow = $true
$notifiedHigh = $false
$lastNotificationTime = $now
}
elseif ($batteryLevel -ge $highThreshold -and (-not $notifiedHigh -or ($periodicNotify -and $timeSinceLastNotify -ge $periodicNotifyInterval))) {
Show-ToastNotification "Batteria oltre l'$highThreshold% — puoi scollegare la tastiera."
$notifiedHigh = $true
$notifiedLow = $false
$lastNotificationTime = $now
}
$lastBatteryLevel = $batteryLevel
}
else {
Write-Host "⚠️ Batteria non leggibile, ma tastiera collegata."
}
}
else {
$missingCount++
Write-Host "$(Get-Date -Format "HH:mm:ss") - Tastiera non connessa. Tentativi: $missingCount/$maxMissingChecks"
if ($missingCount -ge $maxMissingChecks) {
Write-Host "🛑 Tastiera scollegata per troppo tempo. Script terminato."
break
}
}
Start-Sleep -Seconds $checkIntervalSeconds
}
}
finally {
if (Test-Path $lockFilePath) {
Remove-Item $lockFilePath -ErrorAction SilentlyContinue
}
Write-Host "✅ Script terminato e file di lock rimosso."
}