release v1.0

This commit is contained in:
2026-07-21 12:39:10 +02:00
parent 2d2cdfad3b
commit a5a2b80d5d
19 changed files with 1245 additions and 2 deletions
+332 -2
View File
@@ -1,3 +1,333 @@
# apagado_seguro
# Apagado Seguro de Instalacion
Este proyecto pretende brindar las herramientas para poder ejecutar un apagado
ordenado y seguro de una instalacion basandose en la monitorizacion de la red
de alimentacion y el estado de una bateria de respaldo, a traves de la informacion
que transmite el Cerbo GX de Victron Energy y con HomeAssistant como coordinador.
Este proyecto pretende brindar las herramientas para poder ejecutar un apagado ordenado y seguro de una instalacion basandose en la monitorizacion de la red de alimentacion y el estado de una bateria de respaldo, a traves de la informacion que transmite el Cerbo GX de Victron Energy y con HomeAssistant como coordinador.
## Resumen del proceso de apagado y encendido
Desde homeassistant se monitoriza los datos que brinda el Cerbo y el
Multiplus (inversor) de Victron. Principalmente fijandose en 2 datos concretos,
la carga de la bateria y el estado de la red de alimentacion.
La automatizacion de apagado tiene 2 fases.
En la primera, la raspi se encarga de apagar los datalogger, equipos de servicio
secundarios como raspis de dinamica, NVR o similar y parar la base de datos.
Esto permite que la sincronizacion con nuestro servidor no se rompa de forma
inesperada.
Esta primera fase se dispara cuando se cumplan las siguientes
condiciones simultaneamente:
- No hay alimentacion
- La carga de la bateria esta por debajo del primer umbral
Tras esta primera fase, la raspi de estatica, que es la que gestiona todo, se
mantiene encendida mientras la bateria no baje de un segundo umbral.
Esto da la opcion de reencender el sistema si vuelve la tension. De esta forma
cuando la bateria vuelva a un nivel estable se ejecutaria una automatizacion
de encendido. Empezando por la base de datos, seguido de volver a dar tension
al resto de equipos encendiendo los Shellys.
En caso de que la carga siguiese bajando, al llegar a un segundo umbral,
se dispararia la segunda fase.
El objetivo principal de esta es apagar la raspi de estatica de forma segura.
Para ello el homeassistant dispara un script de apagado que revisa que se haya
ejecutado la fase 1 (si no lo ha hecho lo hace ahora), sincroniza los discos y
ordena el poweroff del sistema.
A parte de eso, el Cerbo GX cortocircuita su rele 1, que controla el Shelly
que corta la alimentacion de la Raspi, con un delay de 60s para dar tiempo a
la automatizacion de homeassistant a completarse.
De forma similar a como lo haria la raspi, el Cerbo desactivara el rele una vez
la bateria vuelva a tener carga suficiente; cerrando de nuevo el Shelly, dando
alimentacion a la raspi y disparando la automatizacion de encendido.
![Esquema de fases](/docs/images/esquema_eventos.png)
## Descripcion detallada del proyecto
### Arquitectura
![Esquema de Arquitectura](/docs/images/esquema_arquitectura_instalacion.png)
### Fase 1 - Apagado de dataloggers, equipos de servicio y BDD
El objetivo principal de esta primera fase es apagar todos los equipos que
escriban en la base de datos y cerrar la misma de forma segura para evitar
fallos de sincronizacion. Una vez superado el umbral se ejecuta la autoamtizacion `apagado ordenado` de homeassistant. El proceso es el siguiente:
1. Homeassistant apaga los relés Shelly que controlan la alimentacion de los arduinos.
2. Homeassistant lanza el comando `touch /shutdown-signal/apagar` que crea el
archivo `apagar` en la ruta `/shutdown-signal/` del contenedor que está
vinculada con la ruta `/opt/shutdown-signal/` del host. Este archivo funciona
como flag o señal para iniciar el proceso de apagado en el host.
3. En el host está el script [shutdown-watcher.sh](/scripts/shutdown-watcher.sh)
corriendo como servicio. Este script monitoriza la ruta donde homeassistant genera
el archivo de señal y en funcion del nombre del archivo generado ejecuta un script
u otro.
4. La creacion del archivo `apagar` desencadena la ejecucion del script
[orchestrate-shutdown.sh](/scripts/orchestrate-shutdown.sh)
5. Este script empieza ejecutando un segundo script
[service-nodes-shutdown.sh](/scripts/services-nodes-shutdown.sh) encargado de
conectarse a una lista de equipos de servicio por ssh y ejecutar un poweroff
en estos.
6. Despues se ejecuta el script [mariadb-shutdown.sh](/scripts/mariadb-shutdown.sh)
que para el contenedor de `mariadb`, es decir la base de datos. Esto asegura
que los procesos de la misma se finalizan y la sincro se pausa sin errores.
7. Despues de 30s se corta la alimentacion de los equipos de servicios apagando
los Shellys correspondientes.
> [!NOTE] Todos los script tienen una funcion de log que permite ver si se han
> ejecutado correctamente.
> El archivo de log esta en la ruta `/var/log/safe-shutdown.log`
Con eso acabaria la primera fase de apagado.
Mientras no se sobrepase el segundo umbral la Raspi y el homeassistant se
mantendra encendido monitorizando la alimentacion para poder reiniciar todos
los equipos si se volviese a un estado estable por encima del primer umbral.
### Fase 2 - Apagado total
Esta fase es practicamente un clon de la primera (se reejecuta todo por si no
se hubiese ejecutado previamente) con el añadido de parar el homeassistant,
sincronizar los discos de la raspi y apagar la misma. Para poder cortar la
alimentacion de la Raspi una vez esta ya está apagada haremos uso de la funcion
de control de generador de respaldo del Cerbo GX de Victron. Con esta podremos
controlar un relé que mande una señal de apagado al Shelly que corta la
alimentacion de la Raspi con un retardo de 60s para asegurar que el script de
apagado ya haya finalizado. De esta forma el proceso seria el siguiente:
1. Homeassistant apaga los relés de los arduinos.
2. Homeassistant crea el archivo `apagado_total`.
3. Se ejecuta el script
[orchestrate-total-shutdown.sh](/scripts/orchestrate-total-shutdown.sh)
4. Este ejecuta [service-nodes-shutdown.sh](/scripts/service-nodes-shutdown.sh)
y [mariadb-shutdown.sh](/scripts/mariadb-shutdown.sh)
5. Para el homeassistant, sincroniza los discos y ordena el poweroff.
6. El Cerbo GX cuando detecta la bajada de bateria espera 60s y cortocircuita el relé
1 cuya salida normalmente abierta (NA) esta conectada a la entrada del Shelly que
controla la alimentacion. La salida del Shelly estará invertida respecto a su entrada
de forma que cuando reciba una señal positiva se apague.
Hecho esto la reinicializacion dependerá integramente del Cerbo GX que desactivara
la salida del rele 1 una vez la bateria vuelva a un nivel estable. Esto encendera la raspi, la cual a su vez iniciara el proceso de encendido ordenado ya que la bateria deberia estar por encima del primer umbral.
### Encendido Ordenado
De forma similar al resto esta es una automatizacion que se ejecuta una vez la
bateria supera el umbral establecido para el encendido, en este caso en sentido
positivo.
De nuevo se crea un archivo de señal con el nombre de `encender` que
desencadena la ejecucion del script
[orchestrate-startup.sh](/scripts/orchestrate-startup.sh).
El proceso quedaria asi:
1. Homeassistant crea el archivo `encender`.
2. Se ejecuta [orchestrate-startup.sh](/scripts/orchestrate-startup.sh)
que comprueba el estado de la base de datos iniciandola si no lo está.
3. Pasados 30s se encienden los Shellys de la instalacion devolviendo
la alimentacion tanto a los equipos de servicio como a los arduinos.
El encendido de estos deberia ser automático.
Este encendido ordenado permite que la base de datos ya este activa antes de
que el resto de equipos empiece a enviar datos evitando la perdida de la
primera lectura.
## Despliegue
### Requisitos
Software para el control y monitorizacion de procesos:
- Systemd
- Docker
- Homeassistant
- inotify-tools
- Scripts del proyecto
Para la conexion entre nodos:
- Clave SSH sin passphrase generada en el nodo principal (raspi estatica)
con su respectiva clave publica autorizada en cada nodo de servicio.
Hardware:
- Sistema de baterias de Victron con inversor y Cerbo
- Relés con integracion en homeassistant (Shellys)
### Instalación
> [!NOTE]
> Comprobacion rapida de los archivos con sus permisos correspondientes:
> ```bash
> ls -l /usr/local/sbin/db-clean-stop.sh \
> /usr/local/sbin/shutdown-service-nodes.sh \
> /usr/local/sbin/orchestrate-shutdown.sh \
> /usr/local/sbin/orchestrate-startup.sh \
> /usr/local/sbin/orchestrate-full-shutdown.sh \
> /usr/local/sbin/shutdown-watcher.sh
> ls -l /etc/systemd/system/shutdown-watcher.service
> which inotifywait || sudo apt install inotify-tools
> ```
#### Scripts
Todos los scripts deben ir instalados en la carpeta `/usr/local/sbin/` con permisos
de ejecucion.
En caso de que los scripts no esten ya instalados podremos instalarlos
copiandolos a la maquina y lanzando el siguiente comando:
```bash
sudo install -m 755 <script-path> /usr/local/sbin/<script.sh>
```
#### Clave SSH
El script de `service-nodes-shutdown.sh` se conecta por SSH sin contraseña a
los distintos equipos de servicio. Para ello hace falta generar una clave
SSH en el nodo principal (raspi de estatica) e instalar la clave publica correspondiente en cada nodo de
servicio.
Generar clave SSH en el nodo principal:
```bash
sudo ssh-keygen -t ed25519 -f /root/.ssh/id_shutdown -N "" -C "shutdown-orchestrator"
sudo ls -l /root/.ssh/id_shutdown # debe ser -rw------- (600), propietario root
```
Instalar clave publica en un nodo (desde el principal):
```bash
sudo ssh-copy-id -i /root/.ssh/id_shutdown.pub USER_DEL_NODO@IP_DEL_NODO
```
Comprobacion (debe devolver el hostname del nodo de servicio):
```bash
sudo ssh -i /root/.ssh/id_shutdown shutdown-user@IP_DEL_NODO 'hostname'
```
#### Carpeta de señal y volumen de Docker
Los archivos de señal se generan en la carpeta `/opt/shutdown-signal`, sino existe
hay que crearla:
```bash
sudo mkdir -p /opt/shutdown-signal
```
Para que homeassistant pueda escribir en ella hay que montarla como volumen
del contenedor en el `compose.yaml`
```yaml
homeassistant:
...
volumes:
- ...
- /opt/shutdown-signal:/shutdown-signal
```
Despues de añadirlo recrea el volumen:
```bash
docker compose up -d homeassistant # debe decir "Recreating"
```
#### Shell Commands de Homeassistant
Los comandos de shell que crean los archivos que sirven como señal para el
watcher se definen en la configuracion de homeassistant:
```yaml
# configuration.yaml
shell_command:
trigger_shutdown: touch /shutdown-signal/apagar
trigger_total_shutdown: touch /shutdown-signal/apagado_total
trigger_startup: touch /shutdown-signal/encender
```
#### Activar el Watcher
El servicio del watcher `shutdown-watcher.service` debe ir
con permisos de lectura y escritura en la carpeta de systemd
`/etc/systemd/system/`. Si no esta instalado instalar:
```bash
sudo install -m 644 shutdown-watcher.service /etc/systemd/system/shutdown-watcher.service
```
Para activarlo en systemd:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now shutdown-watcher.service
sudo systemctl status shutdown-watcher.service # debe salir "active (running)"
```
#### Estructura final de archivos
De esta forma los archivos correspondientes a este proyecto quedarian de la
siguiente forma:
```bash
# Servicio del watcher
/etc/systemd/system/
└── shutdown-watcher.service
# Carpeta que monitoriza el watcher
/opt/shutdown-signal/
# Homeassistant
/.../servidor-campo/
├── compose.yaml
└── homeassistant/
└── config_ha/
└── configuration.yaml
# Clave SSH
/root/.ssh/
├── id_shutdown
└── id_shutdown.pub
# Scripts
/usr/local/sbin/
├── mariadb-shutdown.sh
├── orchestrate-shutdown.sh
├── orchestrate-startup.sh
├── orchestrate-total-shutdown.sh
├── service-nodes-shutdown.sh
└── shutdown-watcher.sh
```
### Configuracion
Una vez este todo instalado hay distintas configuraciones a realizar en
funcion de la instalacion.
#### Automatizaciones Homeassistant
Como minimo habra que configurar los reles a apagar o encender. Tambien
pueden variar los umbrales, triggers y delays.
Todo esto se puede configurar desde la seccion de
`/configuracion/automatizaciones_y_escenas/` de la web de Homeassistant.
Ejemplos
- [apagado_ordenado_fase1](/docs/examples/apagado_ordenado_fase1.yaml.example)
- [apagado_total_fase2](/docs/examples/apagado_total_fase2.yaml.example)
- [encendido_ordenado](/docs/examples/encendido_ordenado.yaml.example)
#### IP-s nodos de servicio - service-nodes-shutdown.sh
En la cabecera de [service-nodes-shutdown.sh](/scripts/service-nodes-shutdown.sh)
en el array `NODES` se definen el usuario, IP y comando para el apagado de los
distintos nodos de servicio con el siguiente formato: `user@IP|command`
```bash
# service-nodes-shutdown.sh
NODES=(
"pi@192.168.1.219|sudo /usr/sbin/poweroff" # Raspi Dinamica
"root@192.168.1.50|/sbin/poweroff" # NVR
)
```
#### Cerbo y Shelly del nodo principal
Para el apagado completo debemos encender y configurar la funcion de
`Arranque/parada del generador` del rele 1 del Cerbo GX.
Desde su interfaz remota:
- Habilitar:
`/Ajustes/Integraciones/Reles/Funcion (Rele 1)/Arranque/parada del generador`
![Configuracion Cerbo GX](/docs/images/ConfigCerbo.png)
- Configurar:
`/Ajustes/Dispositivos/Grupo Electrogeno/`
- Funcion de arranque automatico -> ON
- Ajustes/Condiciones/Estado de carga de la bateria -> Definir umbrales y retardos
![Umbrales Cerbo GX](/docs/images/UmbralesCerbo.png)
> [!WARNING]
> Esta funcion controlara la alimentacion del nodo principal cortando cualquier
> proceso que se pueda estar ejecutando en el mismo. Conviene definir un
> retardo suficientemente amplio para que el proceso de apagado total finalice
> correctamente.
Una vez cofigurado esto debemos conectar la salida NA del rele 1 del Cerbo a una
entrada del Shelly y configurar la accion de la misma en este:
- OUT Input/Output settings:
- Input mode -> Switch
- Output type -> Toggle
- IN Invert -> ON
> [!NOTE]
> La funcion de Arranque/Parada del generador de Cerbo esta pensada para encender un
> generador cuando la bateria baje de un umbral pero nosotros lo usamos justamente
> para lo contrario, apagar el nodo principal, es por esto que la entrada del
> Shelly se invierte.