release v1.0
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Descripcion detallada del proyecto
|
||||||
|
|
||||||
|
### Arquitectura
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### 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`
|
||||||
|

|
||||||
|
- Configurar:
|
||||||
|
`/Ajustes/Dispositivos/Grupo Electrogeno/`
|
||||||
|
- Funcion de arranque automatico -> ON
|
||||||
|
- Ajustes/Condiciones/Estado de carga de la bateria -> Definir umbrales y retardos
|
||||||
|

|
||||||
|
|
||||||
|
> [!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.
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
services:
|
||||||
|
mariadb:
|
||||||
|
container_name: mariadb
|
||||||
|
image: mariadb:11
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- MARIADB_ROOT_PASSWORD=Ant3na2021
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
ports:
|
||||||
|
- "3307:3306"
|
||||||
|
volumes:
|
||||||
|
- mariadb_data:/var/lib/mysql
|
||||||
|
- ./mariadb/init:/docker-entrypoint-initdb.d:ro
|
||||||
|
- ./mariadb/conf.d/replication.cnf:/etc/mysql/mariadb.conf.d/99-replication.cnf:ro
|
||||||
|
networks:
|
||||||
|
- teknes-net
|
||||||
|
|
||||||
|
phpmyadmin:
|
||||||
|
container_name: phpmyadmin
|
||||||
|
image: phpmyadmin:latest
|
||||||
|
restart: always
|
||||||
|
# ports:
|
||||||
|
# - "80:80"
|
||||||
|
environment:
|
||||||
|
- PMA_HOST=mariadb
|
||||||
|
- PMA_PORT=3306
|
||||||
|
- PMA_ABSOLUTE_URI=http://localhost/phpmyadmin/
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
networks:
|
||||||
|
- teknes-net
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
container_name: nginx
|
||||||
|
image: nginx:alpine
|
||||||
|
restart: always
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
depends_on:
|
||||||
|
- phpmyadmin
|
||||||
|
networks:
|
||||||
|
- teknes-net
|
||||||
|
|
||||||
|
app-servidor-campo:
|
||||||
|
container_name: app-servidor-campo
|
||||||
|
build: ./app-servidor-campo
|
||||||
|
init: true
|
||||||
|
restart: on-failure
|
||||||
|
ports:
|
||||||
|
- "5566:5566"
|
||||||
|
volumes:
|
||||||
|
- ./app-servidor-campo/app:/home/teknes/ServidorCampo
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
networks:
|
||||||
|
- teknes-net
|
||||||
|
|
||||||
|
# mqtt:
|
||||||
|
# container_name: mqtt
|
||||||
|
# image: eclipse-mosquitto
|
||||||
|
# restart: unless-stopped
|
||||||
|
# network_mode: host
|
||||||
|
# volumes:
|
||||||
|
# - ./homeassistant/config_mqtt:/mosquitto/config
|
||||||
|
# - ./homeassistant/data_mqtt:/mosquitto/data
|
||||||
|
# - ./homeassistant/log_mqtt:/mosquitto/log
|
||||||
|
# environment:
|
||||||
|
# - TZ=Europe/Madrid
|
||||||
|
|
||||||
|
homeassistant:
|
||||||
|
container_name: homeassistant
|
||||||
|
image: ghcr.io/home-assistant/home-assistant:stable
|
||||||
|
restart: always
|
||||||
|
privileged: true
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
- ./homeassistant/config_ha:/config
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /run/dbus:/run/dbus:ro
|
||||||
|
- /opt/shutdown-signal:/shutdown-signal # Fichero de señal de apagado
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
depends_on:
|
||||||
|
# - mqtt
|
||||||
|
- file-editor
|
||||||
|
|
||||||
|
file-editor:
|
||||||
|
container_name: file-editor
|
||||||
|
image: causticlab/hass-configurator-docker:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
- ./homeassistant/config_file-editor:/config
|
||||||
|
- ./homeassistant/config_ha:/hass-config
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Madrid
|
||||||
|
|
||||||
|
# node-red:
|
||||||
|
# container_name: node-red
|
||||||
|
# image: nodered/node-red:latest
|
||||||
|
# restart: unless-stopped
|
||||||
|
# network_mode: host
|
||||||
|
# volumes:
|
||||||
|
# - ./homeassistant/data_nodered:/data
|
||||||
|
# environment:
|
||||||
|
# - TZ=Europe/Madrid
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mariadb_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
teknes-net:
|
||||||
|
driver: bridge
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
# Loads default set of integrations. Do not remove.
|
||||||
|
default_config:
|
||||||
|
|
||||||
|
http:
|
||||||
|
use_x_forwarded_for: true
|
||||||
|
trusted_proxies:
|
||||||
|
- 172.18.0.0/16
|
||||||
|
|
||||||
|
panel_iframe:
|
||||||
|
configurator:
|
||||||
|
title: Configurator
|
||||||
|
icon: mdi:wrench
|
||||||
|
#url: http://192.168.1.220:3218
|
||||||
|
url: http://ncie-benamira:54753
|
||||||
|
|
||||||
|
shell_command:
|
||||||
|
trigger_shutdown: touch /shutdown-signal/apagar
|
||||||
|
trigger_total_shutdown: touch /shutdown-signal/apagado_total
|
||||||
|
trigger_startup: touch /shutdown-signal/encender
|
||||||
|
|
||||||
|
# zigbee2mqtt:
|
||||||
|
# title: Zigbee2mqtt
|
||||||
|
# icon: mdi:zigbee
|
||||||
|
# #url: http://192.168.1.220:8080
|
||||||
|
# url: http://teknes-instalacion.ddns.net:54754
|
||||||
|
|
||||||
|
#switch:
|
||||||
|
# - platform: group
|
||||||
|
# name: Instalacion
|
||||||
|
# entities:
|
||||||
|
# - switch.caja_instalacion
|
||||||
|
#
|
||||||
|
# - platform: group
|
||||||
|
# name: Cajas
|
||||||
|
# entities:
|
||||||
|
# - switch.caja_eje1_pila1_204_210
|
||||||
|
# - switch.caja_eje1_pila2_205_211
|
||||||
|
# - switch.caja_eje1_pila3_201
|
||||||
|
# - switch.caja_eje2_pila1_203_212
|
||||||
|
# - switch.caja_eje2_pila2_206_213
|
||||||
|
# - switch.caja_eje2_pila3_202
|
||||||
|
|
||||||
|
# - platform: mqtt
|
||||||
|
# name: "Caja instalacion"
|
||||||
|
# state_topic: "zigbee2mqtt/caja-instalacion/status"
|
||||||
|
# command_topic: "zigbee2mqtt/caja-instalacion"
|
||||||
|
# payload_on: "ON"
|
||||||
|
# payload_off: "OFF"
|
||||||
|
# qos: 0
|
||||||
|
# retain: true
|
||||||
|
# restore_state: true
|
||||||
|
#
|
||||||
|
# - platform: mqtt
|
||||||
|
# name: "Caja eje1 pila1 204 210"
|
||||||
|
# state_topic: "zigbee2mqtt/caja-eje1-pila1-204-210/status"
|
||||||
|
# command_topic: "zigbee2mqtt/caja-eje1-pila1-204-210"
|
||||||
|
# payload_on: "ON"
|
||||||
|
# payload_off: "OFF"
|
||||||
|
# qos: 0
|
||||||
|
# retain: true
|
||||||
|
# restore_state: true
|
||||||
|
#
|
||||||
|
# - platform: mqtt
|
||||||
|
# name: "Caja eje1 pila2 205 211"
|
||||||
|
# state_topic: "zigbee2mqtt/caja-eje1-pila2-205-211/status"
|
||||||
|
# command_topic: "zigbee2mqtt/caja-eje1-pila2-205-211"
|
||||||
|
# payload_on: "ON"
|
||||||
|
# payload_off: "OFF"
|
||||||
|
# qos: 0
|
||||||
|
# retain: true
|
||||||
|
# restore_state: true
|
||||||
|
#
|
||||||
|
# - platform: mqtt
|
||||||
|
# name: "Caja eje1 pila3 201"
|
||||||
|
# state_topic: "zigbee2mqtt/caja-eje1-pila3-201/status"
|
||||||
|
# command_topic: "zigbee2mqtt/caja-eje1-pila3-201"
|
||||||
|
# payload_on: "ON"
|
||||||
|
# payload_off: "OFF"
|
||||||
|
# qos: 0
|
||||||
|
# retain: true
|
||||||
|
# restore_state: true
|
||||||
|
#
|
||||||
|
# - platform: mqtt
|
||||||
|
# name: "Caja eje2 pila1 203 212"
|
||||||
|
# state_topic: "zigbee2mqtt/caja-eje2-pila1-203-212/status"
|
||||||
|
# command_topic: "zigbee2mqtt/caja-eje2-pila1-203-212"
|
||||||
|
# payload_on: "ON"
|
||||||
|
# payload_off: "OFF"
|
||||||
|
# qos: 0
|
||||||
|
# retain: true
|
||||||
|
# restore_state: true
|
||||||
|
#
|
||||||
|
# - platform: mqtt
|
||||||
|
# name: "Caja eje2 pila2 206 213"
|
||||||
|
# state_topic: "zigbee2mqtt/caja-eje2-pila2-206-213/status"
|
||||||
|
# command_topic: "zigbee2mqtt/caja-eje2-pila2-206-213"
|
||||||
|
# payload_on: "ON"
|
||||||
|
# payload_off: "OFF"
|
||||||
|
# qos: 0
|
||||||
|
# retain: true
|
||||||
|
# restore_state: true
|
||||||
|
#
|
||||||
|
# - platform: mqtt
|
||||||
|
# name: "Caja eje2 pila3 202"
|
||||||
|
# state_topic: "zigbee2mqtt/caja-eje2-pila3-202/status"
|
||||||
|
# command_topic: "zigbee2mqtt/caja-eje2-pila3-202"
|
||||||
|
# payload_on: "ON"
|
||||||
|
# payload_off: "OFF"
|
||||||
|
# qos: 0
|
||||||
|
# retain: true
|
||||||
|
# restore_state: true
|
||||||
|
|
||||||
|
#
|
||||||
|
#telegram_bot:
|
||||||
|
# - platform: polling
|
||||||
|
# api_key: 6092066180:AAHJOCNkbjnNg5NUcEOL2kjLDKd7AFecu3s
|
||||||
|
# allowed_chat_ids:
|
||||||
|
# #335322529:sergio, 5735162:Iñigo
|
||||||
|
# - 335322529
|
||||||
|
# - 5735162
|
||||||
|
|
||||||
|
#mqtt:
|
||||||
|
# discovery: true
|
||||||
|
# #broker: mqtt://192.168.1.220
|
||||||
|
# broker: mqtt://teknes-instalacion.ddns.net:54755
|
||||||
|
# birth_message:
|
||||||
|
# topic: 'homeassistant/status'
|
||||||
|
# payload: 'online'
|
||||||
|
# will_message:
|
||||||
|
# topic: 'homeassistant/status'
|
||||||
|
# payload: 'offline'
|
||||||
|
|
||||||
|
# Load frontend themes from the themes folder
|
||||||
|
#frontend:
|
||||||
|
#themes: !include_dir_merge_named themes
|
||||||
|
|
||||||
|
# Text to speech
|
||||||
|
tts:
|
||||||
|
- platform: google_translate
|
||||||
|
|
||||||
|
|
||||||
|
api:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
automation: !include automations.yaml
|
||||||
|
script: !include scripts.yaml
|
||||||
|
scene: !include scenes.yaml
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Watcher de senal de apagado ordenado
|
||||||
|
After=network.target docker.service
|
||||||
|
Wants=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/sbin/shutdown-watcher.sh
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
alias: Apagado Ordenado FASE 1
|
||||||
|
description: ""
|
||||||
|
# Trigger
|
||||||
|
triggers:
|
||||||
|
- trigger: numeric_state
|
||||||
|
entity_id: # Entidad que controla el trigger
|
||||||
|
- sensor.pylontech_battery_id_512_carga
|
||||||
|
below: 99 # Umbral
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
# Apagado de datalogger
|
||||||
|
- action: switch.turn_off
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id: # Relés dataloggers
|
||||||
|
- switch.instalacion
|
||||||
|
- ...
|
||||||
|
data: {}
|
||||||
|
# Comando que genera el archivo de señal "apagar"
|
||||||
|
- action: shell_command.trigger_shutdown
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
# Retardo de 1 min
|
||||||
|
- delay:
|
||||||
|
hours: 0
|
||||||
|
minutes: 1
|
||||||
|
seconds: 0
|
||||||
|
milliseconds: 0
|
||||||
|
# Apagado de nodos de servicio
|
||||||
|
- action: switch.turn_off
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id: # Reles nodos de servicio
|
||||||
|
- switch.dinamica_principal
|
||||||
|
- switch.nvr_nvr
|
||||||
|
data: {}
|
||||||
|
mode: single
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
alias: Apagado TOTAL FASE 2
|
||||||
|
description: ""
|
||||||
|
# Trigger
|
||||||
|
triggers:
|
||||||
|
- trigger: numeric_state
|
||||||
|
entity_id: # Entidad que controla el tigger
|
||||||
|
- sensor.pylontech_battery_id_512_carga
|
||||||
|
below: 95 # Umbral
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
# Apagado de datalogger
|
||||||
|
- action: switch.turn_off
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id: # Reles dataloggers
|
||||||
|
- switch.instalacion
|
||||||
|
- ...
|
||||||
|
data: {}
|
||||||
|
# Comando que genera el archivo de señal "apagado_total"
|
||||||
|
- action: shell_command.trigger_total_shutdown
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
# Retardo de 1 min
|
||||||
|
- delay:
|
||||||
|
hours: 0
|
||||||
|
minutes: 1
|
||||||
|
seconds: 0
|
||||||
|
milliseconds: 0
|
||||||
|
# Apagado de nodos de servicio
|
||||||
|
- action: switch.turn_off
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id: # Reles nodos de servicio
|
||||||
|
- switch.dinamica_principal
|
||||||
|
- switch.nvr_nvr
|
||||||
|
data: {}
|
||||||
|
mode: single
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
alias: Encendido Ordenado
|
||||||
|
description: ""
|
||||||
|
# Trigger
|
||||||
|
triggers:
|
||||||
|
- trigger: numeric_state
|
||||||
|
entity_id:
|
||||||
|
- sensor.pylontech_battery_id_512_carga
|
||||||
|
above: 98
|
||||||
|
conditions: []
|
||||||
|
actions:
|
||||||
|
# Comando que genera el archivo de señal "encender"
|
||||||
|
- action: shell_command.trigger_startup
|
||||||
|
metadata: {}
|
||||||
|
data: {}
|
||||||
|
# Encendido nodos de servicio
|
||||||
|
- action: switch.turn_on
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- switch.dinamica_principal
|
||||||
|
- switch.nvr_nvr
|
||||||
|
data: {}
|
||||||
|
# Retardo 30s
|
||||||
|
- delay: "00:00:30"
|
||||||
|
# Encendido de dataloggers
|
||||||
|
- action: switch.turn_on
|
||||||
|
metadata: {}
|
||||||
|
target:
|
||||||
|
entity_id:
|
||||||
|
- switch.instalacion
|
||||||
|
- ...
|
||||||
|
data: {}
|
||||||
|
mode: single
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
@@ -0,0 +1,103 @@
|
|||||||
|
<mxfile host="app.diagrams.net">
|
||||||
|
<diagram name="Página-1" id="Rbo6d7JlExwHucXn9K6S">
|
||||||
|
<mxGraphModel dx="1949" dy="1137" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0" />
|
||||||
|
<mxCell id="1" parent="0" />
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-1" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=light-dark(#1BA1E2,#0066CC);strokeColor=#006EAF;fontColor=light-dark(#FFFFFF,#FFFFFF);" value="<div>SISTEMA VICTRON</div><div>(Bateria + Inversor + ...)</div>" vertex="1">
|
||||||
|
<mxGeometry height="80" width="160" x="40" y="200" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-30" edge="1" parent="1" source="ZHIbSSpnAtk2rk2qm88V-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=0;" target="ZHIbSSpnAtk2rk2qm88V-24">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-34" edge="1" parent="1" source="ZHIbSSpnAtk2rk2qm88V-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=0;" target="ZHIbSSpnAtk2rk2qm88V-25">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-2" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" value="" vertex="1">
|
||||||
|
<mxGeometry height="200" width="280" x="320" y="160" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-4" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;" value="<div><span style="white-space: pre;">	</span>Nodo Principal (Raspi Estatica)</div>" vertex="1">
|
||||||
|
<mxGeometry height="40" width="280" x="320" y="160" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-5" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="<div>MariaDB</div><div>(Docker)</div>" vertex="1">
|
||||||
|
<mxGeometry height="60" width="120" x="340" y="280" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-6" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="<div>HomeAssistant</div><div>(Docker)</div>" vertex="1">
|
||||||
|
<mxGeometry height="60" width="110" x="470" y="280" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-7" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" value="<div>Watcher + Scripts</div><div>(Host)</div>" vertex="1">
|
||||||
|
<mxGeometry height="60" width="240" x="340" y="200" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-13" parent="1" style="rounded=1;whiteSpace=wrap;html=1;dashed=1;dashPattern=8 8;fillColor=none;" value="" vertex="1">
|
||||||
|
<mxGeometry height="130" width="210" x="740" y="130" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-14" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" value="Raspi Dinamica" vertex="1">
|
||||||
|
<mxGeometry height="50" width="80" x="760" y="140" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-15" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" value="NVR" vertex="1">
|
||||||
|
<mxGeometry height="50" width="80" x="760" y="200" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-16" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#b0e3e6;strokeColor=#0e8088;" value="..." vertex="1">
|
||||||
|
<mxGeometry height="50" width="80" x="850" y="140" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-17" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Maquinas de Servicio" vertex="1">
|
||||||
|
<mxGeometry height="30" width="130" x="750" y="100" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-18" parent="1" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;fillColor=none;" value="" vertex="1">
|
||||||
|
<mxGeometry height="130" width="210" x="740" y="310" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-19" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" value="Caja 1" vertex="1">
|
||||||
|
<mxGeometry height="50" width="80" x="760" y="320" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-20" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" value="Caja 2" vertex="1">
|
||||||
|
<mxGeometry height="50" width="80" x="760" y="380" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-21" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" value="Caja 3" vertex="1">
|
||||||
|
<mxGeometry height="50" width="80" x="850" y="320" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-22" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fad7ac;strokeColor=#b46504;" value="..." vertex="1">
|
||||||
|
<mxGeometry height="50" width="80" x="850" y="380" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-23" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Instalacion / Dataloggers" vertex="1">
|
||||||
|
<mxGeometry height="30" width="140" x="750" y="280" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-24" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#0050ef;fontColor=#ffffff;strokeColor=#001DBC;" value="<div>Reles&nbsp;</div><div>Shellys</div>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="80" x="640" y="140" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-25" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#0050ef;fontColor=#ffffff;strokeColor=#001DBC;" value="<div>Reles&nbsp;</div><div>Shellys</div>" vertex="1">
|
||||||
|
<mxGeometry height="70" width="80" x="640" y="320" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-26" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#0050ef;fontColor=#ffffff;strokeColor=#001DBC;" value="<div>Reles&nbsp;</div><div>Shellys</div>" vertex="1">
|
||||||
|
<mxGeometry height="45" width="80" x="220" y="217.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-33" edge="1" parent="1" source="ZHIbSSpnAtk2rk2qm88V-24" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.006;entryY=0.239;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" target="ZHIbSSpnAtk2rk2qm88V-13">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-36" edge="1" parent="1" source="ZHIbSSpnAtk2rk2qm88V-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=-0.001;entryY=0.192;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" target="ZHIbSSpnAtk2rk2qm88V-18">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-37" edge="1" parent="1" source="ZHIbSSpnAtk2rk2qm88V-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=-0.003;entryY=0.405;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" target="ZHIbSSpnAtk2rk2qm88V-2">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-38" edge="1" parent="1" source="ZHIbSSpnAtk2rk2qm88V-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=-0.035;entryY=0.479;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=none;endFill=0;" target="ZHIbSSpnAtk2rk2qm88V-26">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-43" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||||
|
<mxGeometry height="180" width="120" x="80" y="360" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-10" parent="ZHIbSSpnAtk2rk2qm88V-43" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#bac8d3;strokeColor=#23445d;" value="" vertex="1">
|
||||||
|
<mxGeometry height="80" width="120" y="100" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-40" parent="ZHIbSSpnAtk2rk2qm88V-43" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#bac8d3;strokeColor=#23445d;" value="Servidor Oficina" vertex="1">
|
||||||
|
<mxGeometry height="80" width="120" y="50" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-41" parent="ZHIbSSpnAtk2rk2qm88V-43" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#bac8d3;strokeColor=#23445d;" value="" vertex="1">
|
||||||
|
<mxGeometry height="80" width="120" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="ZHIbSSpnAtk2rk2qm88V-44" edge="1" parent="1" source="ZHIbSSpnAtk2rk2qm88V-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;dashPattern=12 12;endArrow=none;endFill=0;" target="ZHIbSSpnAtk2rk2qm88V-5">
|
||||||
|
<mxGeometry relative="1" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,115 @@
|
|||||||
|
<mxfile host="app.diagrams.net">
|
||||||
|
<diagram name="Página-1" id="zj4EZcJ4NP1zZ97JjuqS">
|
||||||
|
<mxGraphModel dx="682" dy="398" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0" />
|
||||||
|
<mxCell id="1" parent="0" />
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-10" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" value="" vertex="1">
|
||||||
|
<mxGeometry height="200" width="180" x="560" y="80" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-9" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" value="" vertex="1">
|
||||||
|
<mxGeometry height="200" width="80" x="280" y="80" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-8" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" value="" vertex="1">
|
||||||
|
<mxGeometry height="200" width="80" x="120" y="80" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-3" parent="1" style="shape=partialRectangle;whiteSpace=wrap;html=1;top=0;left=0;fillColor=none;rotation=90;" value="" vertex="1">
|
||||||
|
<mxGeometry height="675" width="237.5" x="300" y="-179.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-5" edge="1" parent="1" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="67.5" y="158.25" as="sourcePoint" />
|
||||||
|
<mxPoint x="767.5" y="158.25" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-6" edge="1" parent="1" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="64" y="240" as="sourcePoint" />
|
||||||
|
<mxPoint x="764" y="240" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-12" edge="1" parent="1" style="endArrow=none;html=1;rounded=1;curved=0;strokeWidth=3;strokeColor=light-dark(#000000,#FF3333);" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="80" y="80" as="sourcePoint" />
|
||||||
|
<mxPoint x="210" y="80" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-14" edge="1" parent="1" source="XWSsugYarvkDZePYYIKa-9" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;strokeColor=light-dark(#000000,#FF3333);" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="390" y="350" as="sourcePoint" />
|
||||||
|
<mxPoint x="210" y="80" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-15" edge="1" parent="1" source="XWSsugYarvkDZePYYIKa-9" style="endArrow=none;html=1;rounded=0;exitX=1.013;exitY=0.208;exitDx=0;exitDy=0;strokeWidth=3;strokeColor=light-dark(#000000,#FF3333);exitPerimeter=0;" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="350" y="280" as="sourcePoint" />
|
||||||
|
<mxPoint x="280" y="180" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-16" edge="1" parent="1" style="endArrow=none;html=1;rounded=0;strokeWidth=3;strokeColor=light-dark(#000000,#FF3333);" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="470" y="280" as="sourcePoint" />
|
||||||
|
<mxPoint x="360" y="120" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-17" edge="1" parent="1" style="endArrow=none;html=1;rounded=0;strokeWidth=3;strokeColor=light-dark(#000000,#FF3333);" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="743" y="79.19" as="sourcePoint" />
|
||||||
|
<mxPoint x="470" y="280" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-18" parent="1" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" value="" vertex="1">
|
||||||
|
<mxGeometry height="20" width="20" x="254" y="148" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-19" parent="1" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" value="" vertex="1">
|
||||||
|
<mxGeometry height="20" width="20" x="376" y="148" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-21" parent="1" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" value="" vertex="1">
|
||||||
|
<mxGeometry height="20" width="20" x="300" y="148" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-22" parent="1" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" value="" vertex="1">
|
||||||
|
<mxGeometry height="20" width="20" x="432" y="230" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-24" parent="1" style="shape=waypoint;sketch=0;fillStyle=solid;size=6;pointerEvents=1;points=[];resizable=0;rotatable=0;perimeter=centerPerimeter;snapToPoint=1;" value="" vertex="1">
|
||||||
|
<mxGeometry height="20" width="20" x="626" y="148" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-25" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#b1ddf0;strokeColor=#10739e;" value="" vertex="1">
|
||||||
|
<mxGeometry height="10" width="20" x="80" y="299" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-26" edge="1" parent="1" style="endArrow=none;html=1;rounded=0;strokeWidth=3;strokeColor=light-dark(#000000,#FF3333);" value="">
|
||||||
|
<mxGeometry height="50" relative="1" width="50" as="geometry">
|
||||||
|
<mxPoint x="101" y="318" as="sourcePoint" />
|
||||||
|
<mxPoint x="81" y="318" as="targetPoint" />
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-28" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="100" vertex="1">
|
||||||
|
<mxGeometry height="30" width="60" x="29" y="64" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-29" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Umbral 1" vertex="1">
|
||||||
|
<mxGeometry height="30" width="60" y="143" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-30" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="Umbral 2" vertex="1">
|
||||||
|
<mxGeometry height="30" width="60" y="225" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-31" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;rounded=0;" value="<div align="left">Tension ON</div><div align="left">BAT</div>" vertex="1">
|
||||||
|
<mxGeometry height="40" width="70" x="111" y="290" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-32" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="E1" vertex="1">
|
||||||
|
<mxGeometry height="25" width="60" x="220" y="157.5" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-33" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="E2" vertex="1">
|
||||||
|
<mxGeometry height="20" width="40" x="310" y="160" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-34" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="E3" vertex="1">
|
||||||
|
<mxGeometry height="30" width="60" x="372" y="130" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-35" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="E4" vertex="1">
|
||||||
|
<mxGeometry height="20" width="54" x="430" y="220" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="XWSsugYarvkDZePYYIKa-36" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="<div>E5</div>" vertex="1">
|
||||||
|
<mxGeometry height="20" width="60" x="620" y="160" as="geometry" />
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# mariadb_clean_stop.sh - Cierre limipio del contenedor de MariaDB en una maquina
|
||||||
|
|
||||||
|
# CONFIGURACION ---------------------------------------------------------------
|
||||||
|
set -uo pipefail
|
||||||
|
# -u -> si no existe una variable devuelve error en vez de tratarla como cadnea vacia
|
||||||
|
# -o pipefail -> devuelve los errores de cualquier pipe no solo del ultimo
|
||||||
|
|
||||||
|
# VARIABLES
|
||||||
|
CONTAINER="mariadb"
|
||||||
|
GRACE=60
|
||||||
|
LOG="/var/log/safe-shutdown.log"
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# funcion para escribir en el log
|
||||||
|
log() { echo "$(date -Is) [mariadb-safe-stop] $*" | tee -a "$LOG"; }
|
||||||
|
|
||||||
|
# PROGRAMA --------------------------------------------------------------------
|
||||||
|
log "inicio"
|
||||||
|
|
||||||
|
# Comprobacion de que el contenedor esta corriendo
|
||||||
|
# Lista los nombres de los contenedores activos y busca exactamente el nombre de $CONTAINER
|
||||||
|
# Si no lo encuentra escribe un aviso en el log y sale con 0
|
||||||
|
if ! docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||||
|
log "AVISO: el contenedor '$CONTAINER' no esta corriendo. Nada que hacer."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parada del contenedor
|
||||||
|
log "parando contenedor '$CONTAINER' (tiempo de gracia: ${GRACE}s)..."
|
||||||
|
if docker stop -t "$GRACE" "$CONTAINER" >/dev/null; then
|
||||||
|
log "OK: Contenedor parado con exito"
|
||||||
|
else
|
||||||
|
log "ERROR: docker stop falló"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verificacion del estado del contenedor
|
||||||
|
if docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||||
|
log "ERROR: '$CONTAINER' sigue activo despues del stop"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "contenedor '$CONTAINER' detenido correctamente"
|
||||||
|
log "fin"
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# orchestrate-shutdown.sh — secuencia completa de apagado ordenado.
|
||||||
|
# Lo dispara el watcher al detectar un fichero de señal.
|
||||||
|
# Orden:
|
||||||
|
# 1. HA apaga los Shellys de la instalacion
|
||||||
|
# 2. HA crea el fichero de señal
|
||||||
|
# 3. Se inicia este script
|
||||||
|
# 3.1. Apagado de raspis y/o equipos de servicio
|
||||||
|
# 3.2. Parado de MariaDB
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# CONFIGURACION----------------------------------------------------------------
|
||||||
|
LOG="/var/log/safe-shutdown.log"
|
||||||
|
APAGADO_NODOS="/usr/local/sbin/service-nodes-shutdown.sh"
|
||||||
|
APAGADO_BDD="/usr/local/sbin/mariadb-shutdown.sh"
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
log() { echo "$(date -Is) [orchestrate] $*" | tee -a "$LOG"; }
|
||||||
|
|
||||||
|
log "===== SECUENCIA DE APAGADO INICIADA ====="
|
||||||
|
|
||||||
|
# 1. Apagar la(s) raspi(s) de servicio por SSH (espera a que hagan halt).
|
||||||
|
log "paso 1: apagando raspis de servicio..."
|
||||||
|
if "$APAGADO_NODOS"; then
|
||||||
|
log "paso 1 ok"
|
||||||
|
else
|
||||||
|
log "paso 1: aviso, el script para el apagado de los nodos devolvio error"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Cerrar la base de datos limpiamente.
|
||||||
|
log "paso 2: cerrando la base de datos..."
|
||||||
|
if "$APAGADO_BDD"; then
|
||||||
|
log "paso 2 OK"
|
||||||
|
else
|
||||||
|
log "paso 2: ERROR al cerrar la BD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "===== SECUENCIA DE APAGADO COMPLETADA ====="
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# orchestrate-startup.sh — secuencia de arranque tras un rebote (corre en el HOST).
|
||||||
|
# Lo dispara el watcher al detectar el fichero de señal 'encender'.
|
||||||
|
# Orden INVERSO al apagado: primero la BD (para que esté lista antes de que
|
||||||
|
# los escritores vuelvan), luego HA reenciende Shelly/nodos por su cuenta.
|
||||||
|
#
|
||||||
|
# NOTA: el reencendido de los Shelly (Arduinos y raspi de servicio) lo hace la
|
||||||
|
# automatización de HA. Este script solo se ocupa de lo que vive en el host:
|
||||||
|
# arrancar el contenedor de la base de datos y confirmar que responde.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# CONFIG ----------------------------------------------------------------------
|
||||||
|
CONTAINER="mariadb"
|
||||||
|
LOG="/var/log/safe-shutdown.log"
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
log() { echo "$(date -Is) [orchestrate-startup] $*" | tee -a "$LOG"; }
|
||||||
|
|
||||||
|
log "===== SECUENCIA DE ARRANQUE INICIADA ====="
|
||||||
|
|
||||||
|
# ¿Ya está corriendo? (idempotente: por si la señal llega dos veces)
|
||||||
|
if docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
|
||||||
|
log "el contenedor '$CONTAINER' ya está corriendo. Nada que hacer."
|
||||||
|
log "===== SECUENCIA DE ARRANQUE COMPLETADA ====="
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Arrancar el contenedor de la BD.
|
||||||
|
log "arrancando contenedor '$CONTAINER'..."
|
||||||
|
if docker start "$CONTAINER" >/dev/null; then
|
||||||
|
log "docker start devolvió OK"
|
||||||
|
else
|
||||||
|
log "ERROR: docker start falló"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Esperar a que MariaDB acepte conexiones (no basta con que el contenedor
|
||||||
|
# esté 'Up': el servidor tarda unos segundos en estar listo).
|
||||||
|
log "esperando a que MariaDB acepte conexiones..."
|
||||||
|
ready=0
|
||||||
|
for i in {1..30}; do
|
||||||
|
if docker exec "$CONTAINER" mariadb-admin ping --silent &>/dev/null; then
|
||||||
|
ready=1
|
||||||
|
log "MariaDB responde (tras ${i}s)"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$ready" -eq 0 ]]; then
|
||||||
|
log "ERROR: MariaDB no respondió tras 30s"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "===== SECUENCIA DE ARRANQUE COMPLETADA ====="
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# total-shutdown.sh — FASE 2: apagado TOTAL de la raspi principal.
|
||||||
|
# Corre en el HOST. Cierra BD + HA y finalmente apaga la propia máquina.
|
||||||
|
#
|
||||||
|
# ATENCIÓN: la última acción es 'poweroff'. Tras ejecutarse, la raspi se apaga.
|
||||||
|
# Solo volverá a encenderse cuando reciba de nuevo alimentación (corte físico +
|
||||||
|
# retorno de tensión). Si la apagas con corriente aún disponible, NO arrancará
|
||||||
|
# sola: requerirá corte/retorno de alimentación o encendido manual.
|
||||||
|
#
|
||||||
|
# Este script asume que la FASE 1 (Arduinos + raspi de servicio) ya se ejecutó.
|
||||||
|
# Pensado para dispararse solo cuando el SoC cruza el segundo umbral (más bajo)
|
||||||
|
# y la red sigue sin volver.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# CONFIG ----------------------------------------------------------------------
|
||||||
|
DB_CONTAINER="mariadb"
|
||||||
|
HA_CONTAINER="homeassistant"
|
||||||
|
APAGADO_NODOS="/usr/local/sbin/service-nodes-shutdown.sh"
|
||||||
|
APAGADO_BDD="/usr/local/sbin/mariadb-shutdown.sh"
|
||||||
|
LOG="/var/log/safe-shutdown.log"
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
log() { echo "$(date -Is) [full-shutdown] $*" | tee -a "$LOG"; }
|
||||||
|
|
||||||
|
log "===== FASE 2: APAGADO TOTAL INICIADO ====="
|
||||||
|
# 1. Comprobar que los nodos de servicio estan apagados
|
||||||
|
log "paso 1: asegurando el apagado de nodos de servicio..."
|
||||||
|
if "$apagado_nodos"; then
|
||||||
|
log "paso 1: OK, todos los nodos apagados"
|
||||||
|
else
|
||||||
|
log "paso 1: ERROR, el script de apagado de nodos fallo"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Cerrar la BD limpiamente (si sigue corriendo). Reutiliza el script probado.
|
||||||
|
if docker ps --format '{{.Names}}' | grep -qx "$DB_CONTAINER"; then
|
||||||
|
log "paso 2: la BD sigue corriendo, cerrándola..."
|
||||||
|
if "$APAGADO_BDD"; then
|
||||||
|
log "paso 2 OK"
|
||||||
|
else
|
||||||
|
log "paso 2: AVISO, el cierre de BD devolvió error (continuamos igualmente)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "paso 2: la BD ya estaba parada (cerrada en fase 1). Saltando."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Parar Home Assistant limpiamente.
|
||||||
|
if docker ps --format '{{.Names}}' | grep -qx "$HA_CONTAINER"; then
|
||||||
|
log "paso 3: parando Home Assistant..."
|
||||||
|
if docker stop -t 30 "$HA_CONTAINER" >/dev/null; then
|
||||||
|
log "paso 3 OK"
|
||||||
|
else
|
||||||
|
log "paso 3: AVISO, docker stop de HA devolvió error (continuamos)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "paso 3: Home Assistant no estaba corriendo. Saltando."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Forzar que todo baje a disco antes de cortar.
|
||||||
|
log "paso 4: sincronizando discos (sync)..."
|
||||||
|
sync
|
||||||
|
sleep 2
|
||||||
|
sync
|
||||||
|
log "paso 4 OK"
|
||||||
|
|
||||||
|
# 5. Apagar la propia máquina. ESTA ES LA ÚLTIMA LÍNEA QUE SE EJECUTA.
|
||||||
|
log "paso 5: apagando la raspi principal. Hasta luego."
|
||||||
|
log "===== FASE 2: APAGADO TOTAL COMPLETADO (poweroff a continuación) ====="
|
||||||
|
sync
|
||||||
|
systemctl poweroff
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# service-nodes-shutdown.sh - Apagado de la(s) raspi(s) de servicio por SSH de
|
||||||
|
# forma segura. Se ejecuta en la raspi principal. Ordena el poweroff y espera
|
||||||
|
# a que deje de hacer ping antes de continuar. Despues corta la alimentacion
|
||||||
|
# fisica a traves del relé correspondiente.
|
||||||
|
|
||||||
|
# REQUISITOS:
|
||||||
|
# - Claves SSH sin passphrase en /root/.ssh/id_shutdown autorizada en cada nodo
|
||||||
|
# - Regla sudoers NOPASSWD para /usr/sbin/poweroff en cada nodo
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# CONFIGURACION ---------------------------------------------------------------
|
||||||
|
NODES=(
|
||||||
|
"pi@192.168.1.219|sudo /usr/sbin/poweroff" # Raspi Dinamica
|
||||||
|
"root@192.168.1.50|/sbin/poweroff" # NVR
|
||||||
|
)
|
||||||
|
SSH_KEY="/root/.ssh/id_shutdown"
|
||||||
|
POWEROFF_CMD="sudo /usr/sbin/poweroff"
|
||||||
|
TIMEOUT_HALT=60 # Timeout para el apagado
|
||||||
|
LOG="/var/log/safe-shutdown.log"
|
||||||
|
SSH_OPTS="-i $SSH_KEY -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new"
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
log() { echo "$(date -Is) [service-nodes-shutdown] $*" | tee -a "$LOG"; }
|
||||||
|
|
||||||
|
log "inicio (${#NODES[@]} nodo/s)"
|
||||||
|
|
||||||
|
for entry in "${NODES[@]}"; do
|
||||||
|
host_user="${entry%%|*}"
|
||||||
|
host="${host_user#*@}" # extrae la IP/host del NODE
|
||||||
|
cmd="${entry#*|}"
|
||||||
|
log "apagando $host_user ..."
|
||||||
|
|
||||||
|
# Ordenar poweroff por ssh
|
||||||
|
if ssh $SSH_OPTS "$host_user" "$cmd"; then
|
||||||
|
log "$entry: orden de poweroff enviada"
|
||||||
|
else # Si falla avisamos y seguimos (puede no ser un error real)
|
||||||
|
log "$entry: AVISO, el SSH devolvio error"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Esperamos la NO respuesta de ping
|
||||||
|
halted=0
|
||||||
|
for ((i=1; i<TIMEOUT_HALT; i++)); do
|
||||||
|
if ! ping -c1 -W1 "$host" &>/dev/null; then
|
||||||
|
log "$host: apagado confirmado (dejo de responder tras ${i}s)"
|
||||||
|
halted=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$halted" -eq 0 ]]; then
|
||||||
|
log "$host: AVISO, sigue respondiendo tras ${TIMEOUT_HALT}s (apagado no confirmado)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "fin"
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# CONFIGURACION ---------------------------------------------------------------
|
||||||
|
SIGNAL_DIR="/opt/shutdown-signal"
|
||||||
|
SHUTDOWN_ORCHESTRATOR="/usr/local/sbin/orchestrate-shutdown.sh"
|
||||||
|
TOTAL_SHUTDOWN_ORCHESTRATOR="/usr/local/sbin/orchestrate-total-shutdown.sh"
|
||||||
|
STARTUP_ORCHESTRATOR="/usr/local/sbin/orchestrate-startup.sh"
|
||||||
|
LOG="/var/log/safe-shutdown.log"
|
||||||
|
|
||||||
|
# PROGRAMA --------------------------------------------------------------------
|
||||||
|
log() { echo "$(date -Is) [watcher] $*" | tee -a "$LOG"; }
|
||||||
|
|
||||||
|
log "watcher iniciado, vigilando $SIGNAL_DIR/"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
inotifywait -q -e create -e moved_to "$SIGNAL_DIR" |
|
||||||
|
while read -r dir event file; do
|
||||||
|
case "$file" in
|
||||||
|
apagar)
|
||||||
|
log "señal detectada ($file). Lanzando secuencia de apagado."
|
||||||
|
"$SHUTDOWN_ORCHESTRATOR"
|
||||||
|
rm -f "$SIGNAL_DIR/apagar"
|
||||||
|
log "señal consumida y borrada."
|
||||||
|
;;
|
||||||
|
encender)
|
||||||
|
log "señal detectada ($file). Lanzando secuencia de encendido."
|
||||||
|
"$STARTUP_ORCHESTRATOR"
|
||||||
|
rm -f "$SIGNAL_DIR/encender"
|
||||||
|
log "señal consumida y borrada."
|
||||||
|
;;
|
||||||
|
apagado_total)
|
||||||
|
log "señal detectada ($file). Lanzando secuencia de apagado TOTAL."
|
||||||
|
"$TOTAL_SHUTDOWN_ORCHESTRATOR"
|
||||||
|
rm -f "$SIGNAL_DIR/apagado_total"
|
||||||
|
log "señal consumida y borrada."
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user