Questo è il Post 3 — l’ultimo — di una serie di tre articoli sulla sostituzione della stazione base Arlo proprietaria con uno stack auto-ospitato. Nel Post 1 di questa serie ho coperto il livello di rete: come fare in modo che un Netgear Orbi RBR760 si spacci sufficientemente bene per la stazione base Arlo da far connettere, registrare, e continuare a streamare le telecamere. Nel Post 2 di questa serie ho coperto il livello server: lo stack Docker arlo-cam-api, il sidecar arlo-snapshot on-demand, il relay RTSP on-demand tramite MediaMTX, e le tre pull request upstream che ho contribuito per correggere i bug incontrati strada facendo.
In questo post copro il livello Home Assistant — come le quattro telecamere diventano entità di primo piano in HA, come un singolo input_select permette di scambiare l’autonomia della batteria con una presenza istantanea, e come la dashboard Lovelace finisce per assomigliare quasi esattamente all’app Arlo, tranne che ogni riga è sotto il vostro controllo. Il repository di accompagnamento su github.com/mmornati/arlo-base-station contiene tutti i file di configurazione e patch menzionati qui.
Ecco l’architettura su cui ci appoggiamo alla fine di questo post. Lo schema è lo stesso del Post 1; la freccia tratteggiata è la nuova parte — il polling REST che HA esegue contro /device/<serial> sulla porta 5000.
WAN
│
┌──────────▼──────────┐
│ Netgear Orbi RBR760│ isolamento guest
│ 172.14.1.1 (Arlo) │◀──── 4x Arlo VMC4040P (192.168.2.x)
│ 192.168.1.x (LAN) │ WiFi guest, isolata
└──────────┬──────────┘
│ DNAT tcp/4000
┌──────────▼──────────┐
│ Server (mini PC) │
│ 192.168.1.48 │
│ │
│ ┌───────────────┐ │ :5000 REST ───┐ polling HA
│ │ arlo-cam-api │ │ :8000 snaps ───┤ (5 min)
│ │ :4000 / :5000│ │ │
│ └───────────────┘ │ │
│ ┌───────────────┐ │ │
│ │ arlo-snapshot │ │ :8000 ────────┤
│ │ (Flask) │ │ │
│ └───────────────┘ │ │
│ ┌───────────────┐ │ │
│ │ mediamtx │ │ :8554 RTSP ───┤
│ │ on-demand │ │ │
│ └───────────────┘ │ │
└─────────────────────┘ │
│
┌───────────────────────────────────────┘
│
┌───────▼────────┐
│ Home Assistant │
│ 192.168.1.32 │
│ │
│ 4 sensori REST│
│ 4 telecamere │
│ 9 automazioni │
│ 4 button-cards│
│ UI Lovelace │
└────────────────┘Una nota sulla redazione. Come nei Post 1 e 2, i veri numeri di serie delle telecamere, gli indirizzi MAC, e l’IP LAN di produzione del server sono stati sostituiti da placeholder
XXXXXXXXXXXXe un placeholder generico. Il valore ben noto172.14.1.1del gateway Arlo è mantenuto perché fa parte del protocollo wire. La sottorete guest192.168.2.x(dove vivono le telecamere sull’Orbi) è lasciata così com’è perché è il valore predefinito standard dell’Orbi e non rivela nulla di specifico.
Perché i sensori REST (e non pyaarlo / aarlo)#
Se avete mai collegato telecamere Arlo in Home Assistant prima, siete quasi certamente passati per pyaarlo — il client Python Arlo non ufficiale — e l’integrazione Home Assistant aarlo sopra di esso. aarlo è il percorso de facto da anni. Espone batteria, segnale, movimento, suono, pressioni del campanello, ultima cattura, attività recente, e un’entità camera.<nome> per telecamera attraverso un unico config flow amichevole.
Ho tenuto aarlo installato su questo deployment. Per gli ultimi tre anni, le quattro entità sensor.aarlo_battery_level_* erano quelle i cui valori innescavano le notifiche push mobili sul mio telefono. Durante la migrazione, quando riavviavo il server, o le telecamere andavano offline, o stavo testando una nuova PR, quelle notifiche push erano il segnale di pre-allarme che qualcosa non andava. Ma c’è una realtà da ammettere: aarlo è un client cloud. I valori di batteria che riportava venivano dall’API cloud Arlo — pyaarlo si autentica su myapi.arlo.com e legge lo stato dei dispositivi dalla lista cloud, non dalle telecamere né dall’emulatore locale. Nel momento in cui le telecamere non sono più registrate con il cloud Arlo — che è il punto centrale di questa serie — aarlo non ha più nulla da interrogare.
Ma ogni altra entità — ogni sensore, ogni telecamera, ogni switch di arm/disarm, ogni toggle del LED PIR — è 100% REST. Ecco l’intero file di configurazione aarlo.yaml:
version: 1
aarlo:
backend: sseTre righe. Il backend: sse seleziona solo il trasporto dello stream di eventi che pyaarlo usa per la sua connessione cloud (SSE invece dello stream MQTT); non crea una modalità senza sessione e non punta aarlo all’emulatore locale. Tutto ciò che aarlo espone arriva ancora dal cloud Arlo. Una volta che le telecamere non sono più registrate lì, le entità lato cloud non hanno più nulla da riportare — le entità camera, movimento, suono, sirena e snapshot passano a unavailable, e i quattro sensori di batteria che conserviamo si limitano a congelarsi all’ultimo valore riportato dal cloud. Sono un record storico, non un feed in tempo reale.
L’entry in customize.yaml per ognuno impedisce alla card batteria auto-generata di creare un duplicato:
sensor.aarlo_battery_level_entree:
battery_alert_disabled: true
battery_sensor_creation_disabled: true
sensor.aarlo_battery_level_jardin_1:
battery_alert_disabled: true
battery_sensor_creation_disabled: true
sensor.aarlo_battery_level_jardin_2:
battery_alert_disabled: true
battery_sensor_creation_disabled: true
sensor.aarlo_battery_level_portail:
battery_alert_disabled: true
battery_sensor_creation_disabled: trueTutto qui. Tre righe di config aarlo e quattro entry customize. Tutto il resto è REST.
La ragione della separazione è semplice: aarlo non è mai stato progettato per telecamere che non parlano con il cloud Arlo. La sua entità camera.aarlo_* assume il meccanismo di stream cloud Arlo standard, che l’emulatore locale non implementa. I suoi binary_sensor.aarlo_motion_* e binary_sensor.aarlo_sound_* vengono dallo stesso feed cloud. I suoi switch.aarlo_siren_* e switch.aarlo_snapshot_* sono azioni cloud. Niente di tutto ciò funziona quando le telecamere stanno chiacchierando allegramente con arlo-cam-api su 192.168.1.48:4000 e non hanno mai sentito parlare di arlo-api.arlo.com.
Lo strato di sensori REST è anche più flessibile. Voi decidete l’intervallo di polling per sensore. Voi decidete come derivare i binary_sensor. Voi decidete quale attributo diventa una card device-class-battery e quale diventa una riga di glance. E ottenete il documento di stato completo dei 25 campi da /device/<serial> gratis, cosa che aarlo non esporrebbe mai.
Lo strato di sensori REST#
L’intero package è in packages/arlo_cameras.yaml. Il primo blocco sono i quattro sensori REST — uno per telecamera. Ecco il blocco completo per la prima telecamera (Jardin 1); le altre tre sono identiche tranne per il seriale e il friendly name:
sensor:
- platform: rest
name: "Arlo Jardin 1 Status"
resource: "http://192.168.1.48:5000/device/XXXXXXXXXXXX"
value_template: "{{ value_json.BatPercent | int(-1) }}"
unit_of_measurement: "%"
device_class: battery
scan_interval: 300
json_attributes:
- BatPercent
- ChargingState
- SignalStrengthIndicator
- WifiRSSI
- Temperature
- Uptime
- PIREvents
- PIRTriggers
- MotionStreamed
- UserStreamed
- Streamed
- Bat1Volt
- FailedStreams
- CameraOnline
- CameraOffline
- IRLEDsOn
- SpotlightEnabled
- WifiConnectionCount
- SystemFirmwareVersion
- HardwareRevision
- WifiChannel
- PoweredOn
- CriticalBatStatus
- ChargerTech
- BatTechQuattro telecamere, quattro sensori, quattro blocchi copia-incolla. L’intervallo di polling è scan_interval: 300 (5 minuti), che è la frequenza giusta per la batteria — la percentuale di batteria non cambia in modo misurabile in una finestra di 5 minuti su un pannello solare in salute. Il value_template estrae BatPercent dal body JSON e lo converte in int con un valore predefinito di -1 per il caso in cui la telecamera è offline (l’API ritorna un body vuoto quando il device è sconosciuto al server, e int(-1) rende lo stato risultante un sentinel riconoscibile su cui posso instradare nei template — qualunque cosa negativa significa «nessun dato», positiva significa «valore reale»).
Il blocco json_attributes è la magia che rende il resto dell’integrazione economico. Ogni campo ritornato da GET /device/<serial> atterra negli attributi del sensore (la tabella di campi completa è nel Post 2 §8). 25 attributi per telecamera, quattro telecamere, tutti visibili dal pannello Developer Tools → States. Il device_class: battery fa sì che lo stato principale venga mostrato con un’icona di batteria in qualsiasi card che sappia renderla.
I quattro sensori sono nominati in modo coerente: Arlo Jardin 1 Status, Arlo Jardin 2 Status, Arlo Portail Status, Arlo Entree Status. La corrispondenza tra il friendly name e la posizione reale della telecamera (Jardin 1, Jardin 2, Portail, Entrée) è ciò che rende la dashboard leggibile senza un glossario.
Perché quattro sensori e non uno con un attributo
select? Perché la piattaformatemplatedi Home Assistant si aspetta un sensore per attributo. Se mettete tutti i 25 attributi su un sensore, dovete scrivere 25 helper che leggono da esso. Se dividete per telecamera, ottenete 4 × 25 attributi che potete ri-derivare in 4 × 15 sensori template amichevoli — e il namespacing per telecamera mantiene il resto dello YAML leggibile.
I sensori template: derivati#
Il sensore REST grezzo espone 25 attributi per telecamera. L’UI delle card di HA non li raccoglie automaticamente — dovete materializzare ognuno come suo sensore se volete una riga di glance, un’icona di batteria, o una sparkline consapevole dell’unità di misura. Il blocco template: in packages/arlo_cameras.yaml deriva 15 sensori amichevoli per telecamera, tutti letti dal corrispondente sensor.arlo_*_status tramite state_attr().
Ecco il blocco Jardin 1. Le altre tre telecamere seguono lo stesso pattern con il loro nome e seriale:
template:
- sensor:
- name: "Jardin 1 Temperature"
unique_id: arlo_j1_temp
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'Temperature') | int(-99) }}"
- name: "Jardin 1 Charging"
unique_id: arlo_j1_charging
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'ChargingState') | default('Off') }}"
- name: "Jardin 1 Signal"
unique_id: arlo_j1_signal
icon: mdi:wifi
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'SignalStrengthIndicator') | int(0) }}"
- name: "Jardin 1 WiFi RSSI"
unique_id: arlo_j1_rssi
unit_of_measurement: "dBm"
state_class: measurement
icon: mdi:wifi-arrow-up-down
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'WifiRSSI') | int(-99) }}"
- name: "Jardin 1 Uptime"
unique_id: arlo_j1_uptime
unit_of_measurement: "s"
icon: mdi:clock-outline
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'Uptime') | int(0) }}"
- name: "Jardin 1 PIR Events"
unique_id: arlo_j1_pir_events
icon: mdi:motion-sensor
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'PIREvents') | int(0) }}"
- name: "Jardin 1 PIR Triggers"
unique_id: arlo_j1_pir_triggers
icon: mdi:motion-sensor
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'PIRTriggers') | int(0) }}"
- name: "Jardin 1 Motion Streams"
unique_id: arlo_j1_motion_streamed
icon: mdi:video
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'MotionStreamed') | int(0) }}"
- name: "Jardin 1 User Streams"
unique_id: arlo_j1_user_streamed
icon: mdi:video-switch
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'UserStreamed') | int(0) }}"
- name: "Jardin 1 Total Streams"
unique_id: arlo_j1_streamed
icon: mdi:filmstrip
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'Streamed') | int(0) }}"
- name: "Jardin 1 Battery Voltage"
unique_id: arlo_j1_bat_voltage
unit_of_measurement: "V"
device_class: voltage
state_class: measurement
icon: mdi:battery
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'Bat1Volt') | float(0) }}"
- name: "Jardin 1 Failed Streams"
unique_id: arlo_j1_failed_streams
icon: mdi:video-off
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'FailedStreams') | int(0) }}"
- name: "Jardin 1 Camera Online"
unique_id: arlo_j1_online
unit_of_measurement: "s"
icon: mdi:camera
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'CameraOnline') | int(0) }}"
- name: "Jardin 1 WiFi Channel"
unique_id: arlo_j1_wifi_channel
icon: mdi:wifi
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'WifiChannel') | int(0) }}"
- name: "Jardin 1 Firmware"
unique_id: arlo_j1_firmware
icon: mdi:chip
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'SystemFirmwareVersion') | default('unknown') }}"Quindici sensori per telecamera. Quattro telecamere. Sessanta sensori template totali. Il unique_id è la chiave — senza di esso, HA si lamenta di “duplicate entity IDs” quando rinominate o ricaricate il package. Il state_class: measurement sui sensori di temperatura, RSSI, e tensione è ciò che fa sì che il motore di statistiche a lungo termine le tracci sul pannello History.
I valori predefiniti int(-99) e int(-100) sui sensori template di temperatura e RSSI sono valori sentinel per “nessun dato”. Li ho scelti deliberatamente in modo che su un HA appena riavviato, la dashboard mostri -99 °C piuttosto che unknown per un paio di minuti fino a quando il primo ciclo di polling arriva. Le icone dei badge sanno cosa fare con -99 (badge rosso) e l’occhio impara a ignorarlo.
Le altre tre telecamere sono identiche. Il blocco completo è nel repository di accompagnamento a packages/arlo_cameras.yaml.
Lo strato binary_sensor:#
Tre binary_sensor per telecamera, derivati dallo stesso documento di stato. Stato dello spotlight, flag di batteria critica, e stato del LED PIR (che è interessante perché rispecchia l’input_boolean.camera_*_led piuttosto che l’attributo della telecamera — il LED è una superficie di toggle, non un sensore). Ecco il blocco Jardin 1:
- binary_sensor:
- name: "Jardin 1 Spotlight"
unique_id: arlo_j1_spotlight
device_class: light
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'SpotlightEnabled') == true }}"
- name: "Jardin 1 Critical Battery"
unique_id: arlo_j1_critical_bat
device_class: battery
state: "{{ state_attr('sensor.arlo_jardin_1_status', 'CriticalBatStatus') | int(0) > 0 }}"
- name: "Jardin 1 LED"
unique_id: arlo_j1_led
device_class: light
state: "{{ is_state('input_boolean.camera_jardin_1_led', 'on') }}"Il binario Spotlight è una lettura pura dall’attributo della telecamera. Il binario Critical Battery è la stessa cosa — CriticalBatStatus è un intero non-zero quando la telecamera ha flaggato la batteria come critica, quindi un confronto > 0 lo trasforma in un booleano pulito. Il binario LED è l’unico che non è una lettura pura — rispecchia lo stato di input_boolean.camera_jardin_1_led. Questa è la UX desiderata: il toggle sulla dashboard è la fonte di verità, e il binary_sensor lo rispecchia semplicemente.
device_class: light sui binary_sensor Spotlight e LED è ciò che permette loro di apparire come tile del dominio light se mai voleste aggiungerli a una card lights. device_class: battery sul binary_sensor Critical Battery fa sì che il pannello History li colori di rosso e inneschi una entry di event log.
La trinità input_boolean + rest_command + automazione#
Questo è il pattern simmetrico che rende il controllo di arm/disarm e LED affidabile. La stessa forma si ripete: un input_boolean (il toggle visibile all’utente in HA), un rest_command (il POST HTTP che atterra sulla telecamera), e una singola automazione che ascolta il booleano e spara il POST. Otto rest_command totali — due per telecamera.
Ecco i quattro comandi *_arm:
rest_command:
camera_jardin_1_arm:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/arm"
method: POST
content_type: "application/json"
payload: '{"PIRTargetState": "{{ "Armed" if arm else "Disarmed" }}"}'
camera_jardin_2_arm:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/arm"
method: POST
content_type: "application/json"
payload: '{"PIRTargetState": "{{ "Armed" if arm else "Disarmed" }}"}'
camera_portail_arm:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/arm"
method: POST
content_type: "application/json"
payload: '{"PIRTargetState": "{{ "Armed" if arm else "Disarmed" }}"}'
camera_entree_arm:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/arm"
method: POST
content_type: "application/json"
payload: '{"PIRTargetState": "{{ "Armed" if arm else "Disarmed" }}"}'Il payload merita una pausa. L’endpoint della stazione base arlo-cam-api accetta il body del protocollo wire Arlo verbatim, il che significa che la chiave è PIRTargetState (camelCase, esattamente come la telecamera lo invia) e il valore è "Armed" o "Disarmed" (capitalizzato, esattamente come la telecamera si aspetta). Il template {{ ... }} è un’espressione Jinja2 che viene renderizzata con l’argomento arm fornito dal chiamante. Il chiamante qui è l’automazione di sync, che passa arm: true o arm: false dallo stato dell’input_boolean — true diventa "Armed", false diventa "Disarmed".
Armare rispecchia il funzionamento del vero ecosistema Arlo: il PIR è un elemento IR passivo, fisicamente sempre acceso in entrambi gli stati, con un consumo trascurabile. Armare non cambia l’elemento PIR: cambia solo la politica di risposta. Quando una telecamera è Armed e il PIR rileva un movimento, la telecamera sveglia SoC e radio e trasmette circa 10 s di video (PIRAction: Stream, DefaultMotionStreamTimeLimit: 10), poi torna a dormire dopo un motionTimeoutAlert. Quando è Disarmed, ignora l’evento. Quei ~10 s di streaming per ogni evento di movimento sono ciò che consuma davvero la batteria — disarmare è quindi la leva sulla batteria più importante che avete. Due dettagli contano. Innanzitutto, le telecamere si avviano Armed per impostazione predefinita grazie all’opzione DefaultPIRTargetState di arlo-cam-api (valori Armed | Disarmed, valore predefinito Armed), quindi una telecamera appena riavviata si comporta come fuori dalla scatola. In secondo luogo, il toggle arm/disarm ferma all’istante lo streaming attivato dal movimento — ma non è una leva di connettività: quella dipende dall’intervallo del beacon, non dallo stato di armamento.
I quattro comandi *_led seguono la stessa forma ma con un payload diverso:
camera_jardin_1_led:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/pirled"
method: POST
content_type: "application/json"
payload: '{"enabled": {{ led | lower }}, "sensitivity": 80}'
camera_jardin_2_led:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/pirled"
method: POST
content_type: "application/json"
payload: '{"enabled": {{ led | lower }}, "sensitivity": 80}'
camera_portail_led:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/pirled"
method: POST
content_type: "application/json"
payload: '{"enabled": {{ led | lower }}, "sensitivity": 80}'
camera_entree_led:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/pirled"
method: POST
content_type: "application/json"
payload: '{"enabled": {{ led | lower }}, "sensitivity": 80}'Il filtro | lower garantisce che true e false vengano serializzati come booleani JSON anziché le stringhe "True" e "False". Senza di esso, il parser JSON di arlo-cam-api rifiuta il body con un 400. Il sensitivity: 80 è hardcoded — il campo dell’API è 0–100 e 80 è il sweet spot tra “il PIR scatta su ogni foglia” e “il PIR scatta solo su un camion”. Se volete esporlo come slider, il payload del rest_command diventa '"sensitivity": {{ sensitivity }}' e l’automazione passa il valore da un input_number.
I quattro input_boolean per telecamera che pilotano l’automazione sono in packages/arlo_cameras.yaml:
input_boolean:
camera_portail_armed:
name: "Camera Portail Armed"
icon: mdi:shield-lock
camera_portail_led:
name: "Camera Portail PIR LED"
icon: mdi:led-on
camera_entree_armed:
name: "Camera Entree Armed"
icon: mdi:shield-lock
camera_entree_led:
name: "Camera Entree PIR LED"
icon: mdi:led-onLo stesso pattern si ripete per camera_jardin_1_armed, camera_jardin_1_led, camera_jardin_2_armed, camera_jardin_2_led — otto input_boolean totali, due per telecamera.
Le automazioni di sync per le telecamere Portail e Entrée vivono in automations.yaml (il file legacy in questo deployment). Le automazioni di sync delle telecamere Jardin 1 e Jardin 2 sono nel repository di accompagnamento a home-assistant/automations/arlo_sync.yaml. Ecco la sync arm del Portail come esempio rappresentativo:
- id: camera_portail_arm_disarm_sync
alias: "Camera Portail Arm/Disarm Sync"
triggers:
- trigger: state
entity_id: input_boolean.camera_portail_armed
actions:
- choose:
- conditions:
- condition: state
entity_id: input_boolean.camera_portail_armed
state: 'on'
sequence:
- action: rest_command.camera_portail_arm
data:
arm: true
default:
- action: rest_command.camera_portail_arm
data:
arm: false
mode: singleLa sync LED ha la stessa forma con led: true / false invece di arm. Quattro automazioni, otto invocazioni di rest_command, otto toggle totali. L’intero ciclo da “l’utente clicca il toggle in HA” a “lo stato PIR della telecamera cambia” richiede circa 30 ms sulla LAN perché nulla deve fare un round-trip attraverso il cloud Arlo.
La macchina arlo_wake — Il trucco per l’autonomia della batteria#
Questo è il pezzo più importante dell’integrazione, e quello che decide se il parco telecamere dura due settimane o due mesi con una singola carica solare. Lo specchio della UX dell’app Arlo dove ogni telecamera è “sempre lì” è esattamente ciò che scarica le batterie. La realtà con RTSP custom è che la porta RTSP della telecamera è chiusa di default, e svegliarla costa 10–14 secondi e una quantità non trascurabile di batteria. Volete svegliarla solo quando sta realmente succedendo qualcosa.
Quanto non trascurabile? L’ho misurato: con il beacon keepalive a 60 secondi, le telecamere armate e la dashboard che interroga gli snapshot, il parco ha perso circa il 3% di batteria all’ora durante la notte — una carica completa in un giorno e mezzo. Il capitolo sull’autonomia misurata qui sotto riporta tutte le misurazioni. È per questo che esiste il quadrante del wake, ed è per questo che l’intera macchina arlo_wake qui sopra è stata costruita in questo modo.
Il package arlo_wake in packages/arlo_wake.yaml risolve questo con tre componenti: un selettore di modalità, un input di intervallo, e uno script che esegue la pipeline wake-then-snapshot. Poi nove automazioni instradano i trigger di wake in base alla modalità.
Il selettore di modalità#
input_select:
arlo_wake_mode:
name: "Arlo Wake Mode"
icon: mdi:camera-control
options:
- "off"
- "periodic"
- "on-demand"
initial: "periodic"
input_number:
arlo_wake_interval_minutes:
name: "Arlo Wake Interval (min)"
icon: mdi:timer-outline
min: 1
max: 60
step: 1
initial: 15Tre modalità:
off— nessun wake automatico. Le telecamere dormono finché uno dei trigger manuali non parte (allarme PIR tramite webhookarlo-cam-api, o pressione di un pulsante). Ideale per assenze lunghe.periodic(predefinito) — l’automazionearlo_wake_periodicscatta ogni 15 minuti (time_pattern: minutes: "/15") ed esegue la pipeline wake-then-snapshot completa per le quattro telecamere. Tiene le telecamere abbastanza calde che qualsiasi tentativo di connessione RTSP riesce in pochi secondi. L’intervallo di 15 minuti è un compromesso: più corto è più amichevole per RTSP in tempo reale, più lungo è più amichevole per la batteria.on-demand— le quattro automazioniarlo_wake_on_view_<cam>scattano quando l’entità telecamera HA transita daidleastreaming. HA tenta la transizione astreamingsolo quando la card Lovelace è in fase di visualizzazione, quindi il wake avviene esattamente quando l’utente sta guardando. Ideale per dashboard live; pessimo per allarmi a decisione rapida.
L’input_number arlo_wake_interval_minutes vi permette di spingere l’intervallo periodico fino a 60 minuti (ottimo per le vacanze) o di scendere a 1 minuto (ottimo per demo e debug live). Le nove automazioni vi fanno tutte riferimento tramite states('input_number.arlo_wake_interval_minutes') (o, nel caso periodico, il ritmo implicito di 15 min).
I comandi REST di wake#
Quattro comandi *_wake_*, uno per telecamera. Fanno POST di {"active": true, "duration": 1800} a /device/<serial>/userstreamactive con un timeout di 8 secondi — abbastanza lungo perché un wake lento della telecamera riesca comunque, abbastanza corto che una telecamera che non risponde non blocchi lo script:
rest_command:
arlo_wake_jardin_1:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/userstreamactive"
method: POST
content_type: "application/json"
payload: '{"active": true, "duration": 1800}'
timeout: 8
arlo_wake_jardin_2:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/userstreamactive"
method: POST
content_type: "application/json"
payload: '{"active": true, "duration": 1800}'
timeout: 8
arlo_wake_portail:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/userstreamactive"
method: POST
content_type: "application/json"
payload: '{"active": true, "duration": 1800}'
timeout: 8
arlo_wake_entree:
url: "http://192.168.1.48:5000/device/XXXXXXXXXXXX/userstreamactive"
method: POST
content_type: "application/json"
payload: '{"active": true, "duration": 1800}'
timeout: 8Il campo duration: 1800 è il suggerimento di minuti-per-tenere-lo-streaming-aperto. L’emulatore della stazione base lo memorizza in memoria e la porta RTSP della telecamera resta aperta per 30 minuti dopo l’ultimo wake andato a buon fine. Dopo 30 minuti senza client, la telecamera si riaddormenta da sola — esattamente la proprietà on-demand di MediaMTX del Post 2.
I comandi *_snapshot_* sono la stessa idea, ma per il sidecar arlo-snapshot:
arlo_snapshot_jardin_1:
url: "http://192.168.1.48:8000/snapshot/XXXXXXXXXXXX"
method: POST
timeout: 30
arlo_snapshot_jardin_2:
url: "http://192.168.1.48:8000/snapshot/XXXXXXXXXXXX"
method: POST
timeout: 30
arlo_snapshot_portail:
url: "http://192.168.1.48:8000/snapshot/XXXXXXXXXXXX"
method: POST
timeout: 30
arlo_snapshot_entree:
url: "http://192.168.1.48:8000/snapshot/XXXXXXXXXXXX"
method: POST
timeout: 30Il timeout di 30 secondi copre il caso peggiore: la telecamera è completamente addormentata, il POST userstreamactive deve svegliarla (10–14 s), il sidecar deve aprire il flusso RTSP (3–5 s), AV deve decodificare un frame (1–2 s), e l’encoder deve scrivere un JPEG (sub-secondo). 30 s è comodo.
La pipeline script.arlo_wake_all#
L’intera pipeline è uno script. La struttura è wake parallelo → delay di 6 secondi → snapshot parallelo. Il delay di 6 secondi è il numero magico — corrisponde a STREAM_WARMUP_SEC=6 nell’ambiente di arlo-snapshot (Post 2), ed è il tempo di cui la telecamera ha bisogno dopo il POST di wake prima che la porta RTSP sia effettivamente raggiungibile:
script:
arlo_wake_all:
alias: "Arlo Wake All Cameras"
icon: mdi:camera-array
sequence:
- parallel:
- action: rest_command.arlo_wake_jardin_1
- action: rest_command.arlo_wake_jardin_2
- action: rest_command.arlo_wake_portail
- action: rest_command.arlo_wake_entree
- delay: "00:00:06"
- parallel:
- action: rest_command.arlo_snapshot_jardin_1
- action: rest_command.arlo_snapshot_jardin_2
- action: rest_command.arlo_snapshot_portail
- action: rest_command.arlo_snapshot_entreeI quattro POST di wake girano in parallelo. Il delay di 6 secondi è essenziale — senza di esso, i POST di snapshot correrebbero in gara con i wake e la maggior parte andrebbe in timeout. Anche i quattro POST di snapshot girano in parallelo. Tempo totale: 6 s + (tempo di wake della telecamera più lenta) ≈ 16 s. Quattro telecamere, quattro snapshot JPEG freschi, pronti perché le card Lovelace li raccolgano.
Le nove automazioni#
Il wake periodico è il più semplice:
automation:
- id: arlo_wake_periodic
alias: "Arlo Wake Periodic"
description: "Periodically wakes all Arlo cameras to keep them reachable for RTSP"
mode: single
trigger:
- platform: time_pattern
minutes: "/15"
condition:
- condition: state
entity_id: input_select.arlo_wake_mode
state: "periodic"
action:
- action: script.arlo_wake_allIl time_pattern: minutes: "/15" significa ogni 15 minuti (lo / iniziale è la sintassi HA “every N”). La condizione fa il gate dell’azione sulla modalità che è periodic. Se la modalità è off o on-demand, l’automazione non fa nulla.
Le quattro automazioni *_on_view_* scattano quando l’entità telecamera HA transita da idle a streaming. Questa transizione avviene quando la card Lovelace è in fase di visualizzazione e HA sta cercando di aprire il flusso RTSP. Il gateway verso il wake è lo stesso comando REST, ma l’azione è il wake di una sola telecamera, non lo script completo:
- id: arlo_wake_on_view_jardin_1
alias: "Arlo Wake on View - Jardin 1"
mode: single
trigger:
- platform: state
entity_id: camera.garden_arlo_jardin_1
from: "idle"
to: "streaming"
condition:
- condition: state
entity_id: input_select.arlo_wake_mode
state: "on-demand"
action:
- action: rest_command.arlo_wake_jardin_1Le quattro automazioni *_on_view_* (una per telecamera) sono identiche nella forma: osservano camera.garden_arlo_<cam>, fanno il gate su on-demand, e scattano il corrispondente rest_command.arlo_wake_<cam>.
Le quattro automazioni *_on_pir_* sono il pezzo sempre attivo. Osservano il sensore template *_pir_triggers — che è il contatore PIR della telecamera stessa da /device/<serial> — e scattano ogni volta che il contatore si incrementa. Il template above: "{{ states('sensor.arlo_jardin_1_pir_triggers') | int(0) }}" è il trigger “qualsiasi incremento” standard. Le condizioni fanno il gate su tre cose: la modalità che è non off, l’input_boolean.arlo_armed master che è on, e l’input_boolean.camera_<cam>_armed della telecamera che è on:
- id: arlo_wake_on_pir_jardin_1
alias: "Arlo Wake on PIR - Jardin 1"
mode: single
trigger:
- platform: numeric_state
entity_id: sensor.arlo_jardin_1_pir_triggers
above: "{{ states('sensor.arlo_jardin_1_pir_triggers') | int(0) }}"
condition:
- condition: and
conditions:
- condition: not
conditions:
- condition: state
entity_id: input_select.arlo_wake_mode
state: "off"
- condition: state
entity_id: input_boolean.arlo_armed
state: "on"
- condition: state
entity_id: input_boolean.camera_jardin_1_armed
state: "on"
action:
- action: rest_command.arlo_wake_jardin_1Il master arlo_armed più il gate per telecamera camera_<cam>_armed significa che un evento PIR costa batteria solo quando vi interessa davvero quella telecamera in quel momento. Disarmate la telecamera (o l’intero parco) e i wake PIR semplicemente smettono di scattare — lo stesso comportamento del vero sistema Arlo quando impostate una modalità.
Il risultato: in modalità off, le telecamere dormono finché non si triggera qualcosa manualmente. In modalità on-demand, si svegliano quando guardate la card Lovelace. In modalità periodic, si svegliano su una cadenza di 15 minuti E sui trigger PIR. Il selettore di modalità è l’unico selettore che decide quanto aggressivamente le telecamere restano calde.
Il package arlo_wake.yaml completo è nel repository di accompagnamento a packages/arlo_wake.yaml.
Autonomia misurata — i numeri veri#
Tutto ciò che precede è progettato attorno a un vincolo: la batteria della telecamera. Quando ho migrato il parco verso lo stack self-hosted, le impostazioni predefinite erano vicine a “sempre caldo”, e i numeri misurati durante la notte sono stati un campanello d’allarme. Ecco i dati completi, così vedete esattamente quanto costa ogni manopola.
La notte di riferimento: beacon 60 s, armato, wake HA, dashboard aperta#
La notte del test (22:41 → 09:30, 10,8 h) è trascorsa con il beacon keepalive a 60 s, tutte le telecamere armate, il wake periodico HA attivo (15 min) e la dashboard Lovelace aperta — la configurazione con cui la maggior parte delle persone inizierebbe:
| Telecamera | Prima | Dopo | Δ | Tasso |
|---|---|---|---|---|
| Jardin 1 | 42% | 1% | −41% | −3,8%/h |
| Jardin 2 | 47% | 17% | −30% | −2,8%/h |
| Entrée | 80% | 46% | −34% | −3,1%/h |
| Portail | 59% | 100% | +41% | in carica |
Le tre telecamere a batteria hanno perso circa un terzo della loro carica in una sola notte. È una carica completa in circa un giorno e mezzo — insostenibile per un parco alimentato a energia solare.
Ma guardate le colonne della connettività: CameraOffline è rimasto congelato a 37 s, 15 s e 34 s per tutta la notte. Il beacon ha fatto il suo lavoro — nessuna telecamera si è disconnessa dal WiFi. Il consumo notturno quindi non era un problema di connettività: era dominato dalle telecamere armate con viste attive (streaming attivato dal movimento), dalla tempesta di polling HA e dal wake periodico HA — non dal beacon.
Le quattro fonti di consumo#
Ho scomposto il numero notturno in quattro consumi distinti:
- Lo streaming video attivato dal movimento in stato armato. Ogni evento PIR su una telecamera armata sveglia SoC e radio e trasmette circa 10 s di video. Nella finestra di misura sono arrivati 116
pirMotionAlertin totale (Jardin 1 = 30, Jardin 2 = 16, Entrée = 12, Portail = 0) e ognuno è stato seguito ~10 s dopo da unmotionTimeoutAlertconStreamDuration: 10— la prova del nove. La batteria è una funzione della frequenza di movimento nel campo visivo di ogni telecamera, non del beacon: nel test armato successivo Jardin 1, con 30 eventi, è scesa di ~5,8%/h. I wake della CPU del beacon a 60 s esistono (~1–2%/h reali), ma sono secondari rispetto allo streaming di movimento. - La tempesta di polling degli snapshot HA.
arlo-snapshotha registrato 986 richiesteGET /snapshot/in 12 h, con un picco di 658 richieste/ora tra le 18:00 e le 20:00. Il colpevole: le card picture-glance della dashboard. HA continua a fare polling delcamera_proxy/image_urldi ogni card che è mai stata a schermo — anche dopo che la dashboard è stata chiusa. Ogni poll è un wake (se addormentata) più una cattura JPEG. - Il wake periodico HA (
userstreamactive). Conduration: 1800, la finestra dello stream non si chiudeva mai, quindi le telecamere non dormivano mai tra i tick di 15 minuti. Il contatoreUserStreamedè esploso: Jardin 1 536/h, Jardin 2 399/h, Entrée 326/h, Portail 1443/h. Confrontate con la baseline di Entrée a ~5 stream/h → 392/h misurati = un’amplificazione ×77. Il wake scattava molto più spesso del previsto. - Uno snapshot a ogni wake. Ogni ciclo wake-then-snapshot apre una sessione RTSP di circa 30 s e cattura un JPEG. Economico da solo; brutale quando moltiplicato per i tre sopra.
L’esperimento a 200 s: la trappola del deep sleep#
Ho poi portato l’intervallo del beacon all’estremo: BeaconIntervalSeconds: 200, con le telecamere Disarmed.
Risultato in 3 h: 0% di consumo su ogni telecamera. Ma non c’è pranzo gratis — le telecamere non risparmiavano energia, erano addormentate. In deep sleep la radio della telecamera è di fatto spenta, la porta 4000 è chiusa e nulla può raggiungerla. Il beacon scattava puntuale e riceveva “no response” a ogni ciclo. Una telecamera addormentata può essere svegliata solo dal proprio ciclo firmware (2–5 h) o premendo fisicamente il pulsante sync.
Ecco la parte che all’inizio abbiamo interpretato male: il deep sleep era causato dall’intervallo di 200 s, non dal disarmamento. Il test a 60 s di prima era anche in Disarmed e le telecamere sono rimaste connesse tutta la notte. Disarmare ferma solo lo streaming attivato dal movimento; non rende irraggiungibile la telecamera.
Questo ha anche esposto un secondo problema: dopo il riavvio del container, known_devices parte vuoto, quindi il beacon non ha nulla da sondare finché ogni telecamera non si ri-registra da sola. È esattamente il bug che risolve il seeding DeviceDB della fix PR #30 del Post 2.
Il risultato “batteria fantastica” è reale — ma solo perché le telecamere sono irraggiungibili.
Il test ARMATA + 100 s#
Infine ho riarmato il parco e portato il beacon a 100 s. Questa volta ho separato le telecamere per attività di movimento: Portail, armata ma con zero eventi di movimento, è rimasta a 94% → 94% in 2 h 45 = 0% di consumo, rispondendo comunque a ogni beacon da 100 s. Jardin 1, armata con 30 eventi di movimento, è passata da 100% a 84% (−16%) ≈ ~5,8%/h. Un’appunto: la lettura precedente “Portail 100% → 94% ≈ 2%/h” veniva in realtà dall’esecuzione a 60 s, disarmata, non dal test armato. Corretto: la batteria segue gli eventi di movimento per telecamera, e l’armamento non è mai stato un correttivo di connettività.
Il verdetto onesto#
Questo stack non è al livello di Arlo. La vera stazione base è il punto di accesso WiFi, quindi può tenere le telecamere in uno stato attivo a basso consumo sulla propria rete; un beacon TCP ogni 100 s è un’approssimazione più grossolana di tutto questo. Il divario misurato è di circa il 2%/h qui contro settimane o mesi di standby sul vero servizio cloud.
Ma con le manopole qui sopra potete ottenere un parco solare sostenibile:
DefaultPIRTargetState— il commutatore di politica di risposta, non un commutatore di alimentazione.Disarmed: il PIR scatta ma la telecamera ignora l’evento, ~0% di consumo, e resta completamente raggiungibile finché l’intervallo del beacon è sano.Armed: il movimento attiva ~10 s di streaming; una vista trafficata costa ~5,8%/h. Disarmate ciò che non vi serve.BeaconIntervalSeconds— il quadrante di raggiungibilità, non di batteria: tenetelo a 60–100 s. A 200 s le telecamere cadono in deep sleep e restano irraggiungibili per ore — la batteria è governata dall’attività di movimento, non da questo.arlo_wake_mode— il quadrante lato HA della sezione precedente (off/periodic/on-demand).scan_interval: 300sui sensori REST — la percentuale di batteria non cambia in finestre di cinque minuti.- Tenete la dashboard chiusa — le card picture-glance fanno polling di
image_urlin continuazione; quella tempesta di 658 GET/h è il consumo più insidioso perché è invisibile nell’UI di HA.
L’idea chiave: misurate gli eventi di movimento per telecamera, non solo la percentuale di batteria. Se una telecamera consuma ma mostra pochi o nessun evento
pirMotionAlert, cercate altrove — beacon troppo rado (cicli di ri-registrazione), tempeste di polling degli snapshot, o polling HA che tieneUserStreamActive=1. Se consuma e ha molti alert di movimento, il consumo è lo streaming attivato dal movimento in stato armato — disarmatela o riducete la sensibilità.
I template Button-Card#
I template button-card in templates/buttons.yaml sono la superficie di wake manuale. Un pulsante per telecamera, ognuno con una press-action che fa la stessa pipeline wake-then-snapshot dello script, ma per una telecamera alla volta:
- button:
- name: "Arlo Wake Jardin 1"
unique_id: arlo_wake_btn_jardin_1
icon: mdi:camera-wireless
press:
- action: rest_command.arlo_wake_jardin_1
- delay: "00:00:06"
- action: rest_command.arlo_snapshot_jardin_1
- name: "Arlo Wake Jardin 2"
unique_id: arlo_wake_btn_jardin_2
icon: mdi:camera-wireless
press:
- action: rest_command.arlo_wake_jardin_2
- delay: "00:00:06"
- action: rest_command.arlo_snapshot_jardin_2
- name: "Arlo Wake Portail"
unique_id: arlo_wake_btn_portail
icon: mdi:camera-wireless
press:
- action: rest_command.arlo_wake_portail
- delay: "00:00:06"
- action: rest_command.arlo_snapshot_portail
- name: "Arlo Wake Entree"
unique_id: arlo_wake_btn_entree
icon: mdi:camera-wireless
press:
- action: rest_command.arlo_wake_entree
- delay: "00:00:06"
- action: rest_command.arlo_snapshot_entreeLe quattro entità button (button.arlo_wake_jardin_1, ecc.) sono messe in superficie sul pannello Cameras Lovelace come tile “Arlo Wake”. Tap → wake → pausa di 6 secondi → snapshot JPEG fresco. La tile pulsa per la durata del wake, poi si stabilizza con la nuova immagine. L’utente vede un’azione “wake + grab still” in tempo reale che costa circa 16 secondi di tempo a muro e una sessione RTSP di 30 secondi sulla telecamera.
Il template button-card è anche il modo più semplice di mettere in superficie il meccanismo di wake al di fuori della dashboard — potete triggerarlo da un’automazione, uno script, un tag NFC, o un bot Telegram. L’entità button è solo un’entità HA come qualsiasi altra.
Il file templates completo è nel repository di accompagnamento a templates/arlo_buttons.yaml.
Il pannello Cameras Lovelace#
La dashboard è costruita sulla card picture-entity standard, la card glance standard, la card entities standard, e le card input-select / input-number standard. Nessuna card custom è richiesta. La vista è chiamata “Cameras” nella barra laterale.
La vista è disposta come uno stack verticale con una riga per telecamera. La cima della vista ha i controlli globali (selettore di modalità, slider di intervallo). Ogni riga ha, da sinistra a destra:
- Una card
picture-entityper la telecamera. Ilcamera_view: auto(predefinito) mostra l’immagine fissa daarlo-snapshotdi default. Tap o clic sulla card e HA apre il flusso RTSP tramite MediaMTX (rtsp://192.168.1.48:8554/cam1per Jardin 1,cam2per Jardin 2,cam3per Portail,cam4per Entrée). Navigate via e lo stream si chiude automaticamente. Le entità telecamera sonocamera.garden_arlo_jardin_1,camera.garden_arlo_jardin_2,camera.garden_arlo_portail, ecamera.garden_arlo_entree— il prefissogarden_arlo_è il namespace che l’integrazionegenericusa di default. - Una card glance con quattro entità: percentuale di batteria (
sensor.arlo_<cam>_statuscondevice_class: battery), WiFi RSSI (sensor.<cam>_wifi_rssi), temperatura (sensor.<cam>_temperature), e stato di carica (sensor.<cam>_charging). La card glance mette una piccola icona e il valore su una riga, quindi tutti e quattro entrano in una singola striscia orizzontale. - Una riga di switch con due mapping
switch.toggle— uno perinput_boolean.camera_<cam>_armed(l’icona scudo) e uno perinput_boolean.camera_<cam>_led(l’icona LED). Il mapping avviene tramite la piattaforma templateswitch.toggle; l’input_boolean è la fonte di verità, e la UI del toggle è solo una finestra su di essa. - La button-card della sezione precedente (
button.arlo_wake_<cam>). La press-action fa la pipeline wake-then-snapshot. - Una riga di entità badge per i tre binary_sensor: Spotlight (
binary_sensor.<cam>_spotlight), Critical Battery (binary_sensor.<cam>_critical_battery), PIR LED (binary_sensor.<cam>_led). Idevice_class: lightedevice_class: batterydei binary_sensor danno loro le icone di default corrette.
La cima della vista ha due card extra:
input_select.arlo_wake_mode— il selettore di modalità. Tre opzioni:off,periodic,on-demand. Il valore predefinito èperiodic.input_number.arlo_wake_interval_minutes— lo slider di intervallo. Range 1–60, valore predefinito 15. Influenza la cadenza di wake periodico.
L’intera vista è circa 5 righe verticali di card su un browser desktop e 4–5 swipe su un telefono. Le quattro telecamere sono disposte da sinistra a destra su uno schermo largo e impilate verticalmente su un telefono. Le card si ridimensionano automaticamente; nessuna configurazione di media-query è richiesta.
La vista è raggiungibile tramite la barra laterale HA principale — l’entry “Cameras” — e le quattro righe di telecamera sono visibili a colpo d’occhio. Non c’è una vista nidificata, nessun modal, nessun pop-over. L’intero blocco è su un solo schermo.
UX d’uso quotidiano#
Una volta che la dashboard è costruita, il loop di interazione utente è corto e prevedibile:
- Selettore Wake Mode in cima al pannello Cameras.
off(nessun auto-wake, le telecamere dormono finché non le triggerate),periodic(ogni 15 minuti, il valore predefinito),on-demand(wake solo quando la card Lovelace è in fase di visualizzazione). In vacanza, tenete le telecamere armate (il PIR resta attivo) e lasciate la modalità suon-demandoperiodic; disarmate ciò che non vi serve per ridurre il costo del wake. In un giorno normale, lasciate superiodic. - Un evento PIR innesca un wake + snapshot immediato. Le automazioni
arlo_wake_on_pir_<cam>scattano ogni volta che il contatore PIR della telecamera si incrementa — ma solo quando la telecamera (e il parco) è armata. Modalitàoff, o telecamera disarmata → nessun wake. Altrimenti → wake, poi 6 s dopo un JPEG fresco è nello store in memoria del sidecar. La card Lovelace raccoglie la nuova immagine al prossimo tick di refresh. - Wake manuale tramite la button-card. Tap sulla tile “Arlo Wake” per la telecamera che volete. Il pulsante pulsa per ~16 secondi. Un JPEG fresco appare nella card della telecamera. La stessa pipeline gira sia che l’abbiate triggerata dalla dashboard o da un bot Telegram.
- Arm/Disarm tramite gli switch toggle. Il toggle
input_boolean.camera_<cam>_armedsulla dashboard. Toggle off → il comando REST ARM parte conarm: false→ il PIRTargetState della telecamera passa aDisarmed. Toggle on → il comando REST ARM parte conarm: true→ il PIRTargetState della telecamera passa aArmed. L’intero round-trip richiede ~30 ms. - Toggle LED PIR. Stesso pattern di arm/disarm. Il toggle
input_boolean.camera_<cam>_led, l’automazionecamera_<cam>_led_sync, il POSTrest_command.camera_<cam>_led. Il LED sulla parte frontale della telecamera si accende quando il toggle è on.
La cadenza periodica di 15 minuti è il cavallo di battaglia. Tiene le telecamere su un ciclo di wake prevedibile così che la connessione RTSP riesca in ~2 secondi quando tappate sulla card della telecamera. Senza di essa, il primo tentativo RTSP dopo un lungo sonno richiederebbe i 10–14 secondi completi del wake della telecamera, che sembra una pagina congelata.
La modalità periodica è anche la ragione per cui l’integrazione funziona bene durante le demo. Se mostrate la dashboard a qualcuno e lui tappa su una telecamera, il wake è già in volo dall’ultimo tick periodico, quindi lo stream si apre in ~2 secondi. L’esperienza “feels instant” dell’app Arlo è principalmente il wake periodico.
Limitazioni e cosa viene dopo#
Restano alcuni rough edge:
- Nessun CVR. La registrazione video continua è una feature cloud-only. Il setup locale vi dà snapshot on-demand e RTSP on-demand; non vi dà una timeline 24/7. Per quello vi servirebbe un registratore separato (es. Frigate) e anche allora, l’emulatore locale manca lo storico eventi
MotionStreamedche vi permetterebbe di riavvolgere. - Nessuna rilevazione AI. Il sensore PIR scatta su qualsiasi movimento — foglie, fari, ombre. Il cloud Arlo originale ha allarmi intelligenti (persona, veicolo, pacco, animale) che filtrano il rumore. Riprodurlo localmente richiederebbe una pipeline CV (Frigate + Coral, o una API remota), che è fuori scope per questo progetto.
userstreamactivenon persiste attraverso i restart diarlo-cam-api. Quando l’emulatore della stazione base riparte, lo stato in memoria di quali telecamere avevano un user stream attivo è perso. Le telecamere recuperano da sole (rilevano la disconnessione TCP e si ri-registrano), ma la prima chiamatauserstreamactivedopo un restart è più lenta perché il server RTSP deve avviarsi a freddo.- Nessuna configurazione di zona di movimento via API. Le zone di attività sono una feature cloud-only sul firmware Arlo ufficiale. Configurarle richiede l’app Arlo, il che vanifica lo scopo dell’auto-hosting. Un’implementazione di stazione base custom potrebbe in linea di principio spingere definizioni di zona verso la telecamera, ma il protocollo non è documentato.
- Nessun proxy di thumbnail per le registrazioni. Le registrazioni sono salvate in
/recordingscome segmenti video grezzi; non c’è API per recuperare un thumbnail at=10sper una data registrazione. Per ora prendo semplicemente uno snapshot fresco tramitearlo-snapshotquando voglio uno still. aarloè un client cloud; i sensori REST sono la vera fonte della batteria.pyaarlolegge la batteria e lo stato dei dispositivi dall’API cloud Arlo, non dalle telecamere né dall’emulatore locale. Una volta che le telecamere lasciano il cloud, le entitàsensor.aarlo_battery_level_*si congelano all’ultimo valore riportato dal cloud e alla fine passano aunavailable— non c’è alcun percorso locale per aggiornarle. I sensori REST insensor.arlo_<cam>_statusleggono lo stesso attributoBatPercentdirettamente dall’endpoint/device/<serial>dell’emulatore locale, quindi continuano a funzionare senza dipendere dal cloud.
Nessuna di queste è un blocker. Sono nice-to-have a cui arriverò quando ci arrivo.
Chiusura della serie#
Questo è il terzo e ultimo articolo della serie. Dal livello di rete nel Post 1, attraverso i servizi e le PR upstream nel Post 2, fino all’integrazione Home Assistant in questo post, avete ora una sostituzione open-source completa per la stazione base Arlo proprietaria. Ogni pezzo gira sul vostro hardware, ogni riga di configurazione è in version control, ogni contributo upstream è documentato, e l’unico costo ricorrente è l’elettricità per far girare il mini PC.
Il repository di accompagnamento su github.com/mmornati/arlo-base-station contiene ogni file referenziato nei tre post, con le copie di produzione, le patch, il docker-compose, e lo YAML Home Assistant in un unico posto. Forkatelo, inviate PR, aprite issue, e ditemi cosa funziona per voi.
Grazie per aver letto.
