Jump to content

SENSBLUE ATLAS/Home Assistant: Difference between revisions

Created page with "= Home Assistant integration = '''Installing Home Assistant on the SENSBLUE ATLAS / i.Cee²''' '''Objective''' Install Home Assistant in a Docker container, running alongside the stack already present on the ATLAS: * Node-RED * Mosquitto MQTT * Grafana * InfluxDB Home Assistant '''does not replace''' the native stack. It only adds an extra interface/integration layer. '''Scope:''' base installation of Home Assistant, creation of the initial account, and MQTT configu..."
 
No edit summary
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{DISPLAYTITLE:Home Assistant}}
= Home Assistant integration =
= Home Assistant integration =
'''Installing Home Assistant on the SENSBLUE ATLAS / i.Cee²'''
'''Installing Home Assistant on the SENSBLUE ATLAS / i.Cee²'''
Line 44: Line 45:
|Local Mosquitto on 127.0.0.1:1883
|Local Mosquitto on 127.0.0.1:1883
|}
|}
Check the system:<syntaxhighlight lang="bash">
Check the system:<syntaxhighlight lang="bash">cat /etc/os-release | grep PRETTY_NAME
cat /etc/os-release | grep PRETTY_NAME
uname -m
uname -m
df -h /
df -h /</syntaxhighlight>
 
=== 2. Ports used on ATLAS ===
Before and after installation, confirm that the existing stack continues to respond.
{| class="wikitable"
!Service
!Port
|-
|Node-RED
|1880
|-
|Mosquitto MQTT
|1883
|-
|Grafana
|3000
|-
|InfluxDB
|8086
|-
|Home Assistant
|8123
|}
Check the stack's ports:<syntaxhighlight lang="bash">
for p in 1880 1883 3000 8086; do
ss -tlnp | grep ":$p " && echo "port $p OK"
done
</syntaxhighlight>
 
=== 3. Installing Docker ===
Install Docker Engine and the Docker Compose plugin through Docker's official Debian repository.
 
Summary of the process:
 
# Update packages.
# Install dependencies.
# Add the Docker GPG key.
# Add the Docker repository.
# Install Docker Engine and the Docker Compose plugin.
# Test with hello-world.
 
 
Check that Docker is operational:<syntaxhighlight lang="bash">
sudo docker run --rm hello-world
</syntaxhighlight>If <code>apt-get update</code> returns a 404 error for the Docker repository, temporarily use the bookworm line for the Docker repository and repeat the update/installation.
 
Example alternative line:<syntaxhighlight lang="bash">
deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable
</syntaxhighlight>
 
=== 4. Create the Home Assistant project ===
Create the project folder:<syntaxhighlight lang="bash">
sudo mkdir -p /opt/atlas-ha
cd /opt/atlas-ha
</syntaxhighlight>Create the <code>docker-compose.yml</code> file:<syntaxhighlight lang="bash">
sudo tee docker-compose.yml > /dev/null << 'EOF'
services:
    homeassistant:
      container_name: homeassistant
      image: ghcr.io/home-assistant/home-assistant:stable
      network_mode: host
      volumes:
        - ./ha-config:/config
        - /etc/localtime:/etc/localtime:ro
      environment:
        - TZ=Europe/Lisbon
      restart: unless-stopped
EOF
</syntaxhighlight>'''Important notes:'''
{| class="wikitable"
!Option
!Reason
|-
|network_mode: host
|Allows direct access to the port and local Mosquitto at 127.0.0.1:1883
|-
|./ha-config:/config
|Stores Home Assistant configuration in /opt/atlas-ha/ha-config
|-
|restart: unless-stopped
|Automatically restarts Home Assistant after reboot or failure
|-
|TZ=Europe/Lisbon
|Sets the correct time zone
|}
 
=== 5. Start Home Assistant ===
From the <code>/opt/atlas-ha</code> folder:<syntaxhighlight lang="bash">
sudo docker compose up -d
</syntaxhighlight>The first start may take a few minutes as the image is downloaded and Home Assistant initialises the configuration.
 
'''Check container status:'''<syntaxhighlight lang="bash">
sudo docker ps
</syntaxhighlight>'''Check HTTP response:'''<syntaxhighlight lang="bash">
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8123
</syntaxhighlight>'''Expected result:'''
{| class="wikitable"
!Code
!Meaning
|-
|200
|Home Assistant accessible
|-
|302
|Home Assistant accessible, with redirection
|-
|000 connection refused
|May be normal in the first 1-2 minutes; wait and retry
|}
 
=== 6. Initial onboarding ===
Open in a browser on a PC on the same network:<syntaxhighlight lang="bash">
http://<atlas-ip>:8123
</syntaxhighlight>Then:
 
# Create the administrator account.
# Set the location, e.g., Portugal, or skip.
# Complete until you reach the main dashboard.
 
'''From this point, Home Assistant is installed and operational.'''
 
=== 7. Configure MQTT integration in Home Assistant ===
Since the container uses <code>network_mode: host</code>, Home Assistant can access the local Mosquitto via:
{| class="wikitable"
!Field
!Value
|-
|Broker
|127.0.0.1
|-
|Port
|1883
|-
|User/password
|As per Mosquitto configuration; blank if no authentication
|}
In Home Assistant:
 
'''Settings → Devices & services → Add integration → MQTT'''
 
Configure the broker as:
 
'''127.0.0.1:1883'''
 
After adding the MQTT integration, create the following YAML files to declare the entities used in the current installation.
 
=== 8. Create configuration.yaml ===
Enter the Home Assistant configuration folder:<syntaxhighlight lang="bash">
cd /opt/atlas-ha/ha-config
</syntaxhighlight>Create or replace the <code>configuration.yaml</code> file:<syntaxhighlight lang="bash">
sudo tee configuration.yaml > /dev/null << 'EOF'
default_config:
frontend:
  themes: !include_dir_merge_named themes
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
mqtt: !include mqtt.yaml
EOF
</syntaxhighlight>Create the included files if they do not already exist:<syntaxhighlight lang="bash">
sudo touch automations.yaml scripts.yaml scenes.yaml
sudo mkdir -p themes
</syntaxhighlight>
</syntaxhighlight>


=== 2. Ports used on ATLAS ===
=== 9. Create mqtt.yaml ===
Create the file:<syntaxhighlight lang="bash">
cd /opt/atlas-ha/ha-config
 
sudo tee mqtt.yaml > /dev/null << 'EOF'
sensor:
  - name: "ATLAS AI1"
    unique_id: atlas_ai1
    state_topic: "analogInputs/runtime/out"
    value_template: "{{ value_json.task.taskResult.AI1.result | float(0) }}"
    unit_of_measurement: "V"
    device_class: voltage
    state_class: measurement
 
  - name: "ATLAS AI2"
    unique_id: atlas_ai2
    state_topic: "analogInputs/runtime/out"
    value_template: "{{ value_json.task.taskResult.AI2.result | float(0) }}"
    unit_of_measurement: "V"
    device_class: voltage
    state_class: measurement
 
  - name: "ATLAS AI3"
    unique_id: atlas_ai3
    state_topic: "analogInputs/runtime/out"
    value_template: "{{ value_json.task.taskResult.AI3.result | float(0) }}"
    unit_of_measurement: "V"
    device_class: voltage
    state_class: measurement
 
  - name: "ATLAS AI4"
    unique_id: atlas_ai4
    state_topic: "analogInputs/runtime/out"
    value_template: "{{ value_json.task.taskResult.AI4.result | float(0) }}"
    unit_of_measurement: "V"
    device_class: voltage
    state_class: measurement
 
binary_sensor:
  - name: "ATLAS DI1"
    unique_id: atlas_di1
    state_topic: "digitalInputs/runtime/out"
    value_template: >-
      {% set di = value_json.task.taskResult.DI1 %}
      {% if di.result is defined %}
        {{ 'ON' if di.result == true else 'OFF' }}
      {% else %}
        {{ 'ON' if di.state == 'high' else 'OFF' }}
      {% endif %}
    payload_on: "ON"
    payload_off: "OFF"
 
  - name: "ATLAS DI2"
    unique_id: atlas_di2
    state_topic: "digitalInputs/runtime/out"
    value_template: >-
      {% set di = value_json.task.taskResult.DI2 %}
      {% if di.result is defined %}
        {{ 'ON' if di.result == true else 'OFF' }}
      {% else %}
        {{ 'ON' if di.state == 'high' else 'OFF' }}
      {% endif %}
    payload_on: "ON"
    payload_off: "OFF"
 
  - name: "ATLAS DI3"
    unique_id: atlas_di3
    state_topic: "digitalInputs/runtime/out"
    value_template: >-
      {% set di = value_json.task.taskResult.DI3 %}
      {% if di.result is defined %}
        {{ 'ON' if di.result == true else 'OFF' }}
      {% else %}
        {{ 'ON' if di.state == 'high' else 'OFF' }}
      {% endif %}
    payload_on: "ON"
    payload_off: "OFF"
 
  - name: "ATLAS DI4"
    unique_id: atlas_di4
    state_topic: "digitalInputs/runtime/out"
    value_template: >-
      {% set di = value_json.task.taskResult.DI4 %}
      {% if di.result is defined %}
        {{ 'ON' if di.result == true else 'OFF' }}
      {% else %}
        {{ 'ON' if di.state == 'high' else 'OFF' }}
      {% endif %}
    payload_on: "ON"
    payload_off: "OFF"
 
switch:
  - name: "ATLAS DO1"
    unique_id: atlas_do1
    command_topic: "digitalOutputs/runtime/in"
    state_topic: "digitalOutputs/runtime/out"
    payload_on: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO1":{"state":"high"}}}}'
    payload_off: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO1":{"state":"low"}}}}'
    value_template: "{{ value_json.task.taskResult.DO1.state }}"
    state_on: "high"
    state_off: "low"
    optimistic: false
 
  - name: "ATLAS DO2"
    unique_id: atlas_do2
    command_topic: "digitalOutputs/runtime/in"
    state_topic: "digitalOutputs/runtime/out"
    payload_on: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO2":{"state":"high"}}}}'
    payload_off: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO2":{"state":"low"}}}}'
    value_template: "{{ value_json.task.taskResult.DO2.state }}"
    state_on: "high"
    state_off: "low"
    optimistic: false
 
  - name: "ATLAS DO3"
    unique_id: atlas_do3
    command_topic: "digitalOutputs/runtime/in"
    state_topic: "digitalOutputs/runtime/out"
    payload_on: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO3":{"state":"high"}}}}'
    payload_off: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO3":{"state":"low"}}}}'
    value_template: "{{ value_json.task.taskResult.DO3.state }}"
    state_on: "high"
    state_off: "low"
    optimistic: false
 
  - name: "ATLAS DO4"
    unique_id: atlas_do4
    command_topic: "digitalOutputs/runtime/in"
    state_topic: "digitalOutputs/runtime/out"
    payload_on: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO4":{"state":"high"}}}}'
    payload_off: '{"id":0,"origin":"HA","task":{"action":"SET","taskParams":{"DO4":{"state":"low"}}}}'
    value_template: "{{ value_json.task.taskResult.DO4.state }}"
    state_on: "high"
    state_off: "low"
    optimistic: false
EOF
</syntaxhighlight>'''Notes about this mqtt.yaml'''
{| class="wikitable"
!Entity
!MQTT Topic
!Function
|-
|sensor.atlas_ai1 .. ai4
|analogInputs/runtime/out
|Show analogue readings AI..AI4
|-
|binary_sensor.atlas_di1 .. di4
|digitalInputs/runtime/out
|Show digital input states DI..DI4
|-
|switch.atlas_do1 .. do4
|digitalOutputs/runtime/in/out
|Control and confirm digital outputs DO..DO4
|}
Digital inputs accept two response formats:<syntaxhighlight lang="bash">
{
"result": true
}
</syntaxhighlight>or:<syntaxhighlight lang="bash">
{
"state": "high"
}
</syntaxhighlight>This allows compatibility with different versions of the Node-RED flow.
 
=== 10. Restart and validate configuration ===
'''Restart Home Assistant:'''<syntaxhighlight lang="bash">
cd /opt/atlas-ha
sudo docker compose restart
</syntaxhighlight>'''View logs:'''<syntaxhighlight lang="bash">
sudo docker logs -f homeassistant
</syntaxhighlight>'''Confirm there are no errors related to MQTT or YAML.'''
 
In Home Assistant, check for entities similar to:
{| class="wikitable"
!Entity
!Type
|-
|sensor.atlas_ai1
|Analogue sensor
|-
|sensor.atlas_ai2
|Analogue sensor
|-
|sensor.atlas_ai3
|Analogue sensor
|-
|sensor.atlas_ai4
|Analogue sensor
|-
|binary_sensor.atlas_di1
|Digital input
|-
|binary_sensor.atlas_di2
|Digital input
|-
|binary_sensor.atlas_di3
|Digital input
|-
|binary_sensor.atlas_di4
|Digital input
|-
|switch.atlas_do1
|Digital output
|-
|switch.atlas_do2
|Digital output
|-
|switch.atlas_do3
|Digital output
|-
|switch.atlas_do4
|Digital output
|}
 
=== 11. Simple dashboard in Home Assistant ===
Once the entities exist, you can create a simple card in the Dashboard.
 
'''Example YAML for a Lovelace view:'''<syntaxhighlight lang="yaml">type: vertical-stack
cards:
  - type: entities
    title: Analog Inputs
    entities:
      - sensor.atlas_ai1
      - sensor.atlas_ai2
      - sensor.atlas_ai3
      - sensor.atlas_ai4
 
  - type: entities
    title: Digital Inputs
    entities:
      - binary_sensor.atlas_di1
      - binary_sensor.atlas_di2
      - binary_sensor.atlas_di3
      - binary_sensor.atlas_di4
 
  - type: entities
    title: Digital Outputs
    entities:
      - switch.atlas_do1
      - switch.atlas_do2
      - switch.atlas_do3
      - switch.atlas_do4</syntaxhighlight>
[[File:Home Assistant img 1.png|center|frameless|1037x1037px]]
[[File:Home Assistant img 2.png|center|frameless|1037x1037px]]
 
=== 12. Test MQTT communication ===
Install MQTT client if necessary:<syntaxhighlight lang="bash">
sudo apt-get install -y mosquitto-clients
</syntaxhighlight>View MQTT messages received by Home Assistant:<syntaxhighlight lang="bash">
mosquitto_sub -h 127.0.0.1 -t 'analogInputs/runtime/out' -v
mosquitto_sub -h 127.0.0.1 -t 'digitalInputs/runtime/out' -v
mosquitto_sub -h 127.0.0.1 -t 'digitalOutputs/runtime/out' -v
</syntaxhighlight>Send a test to a digital output:
 
<nowiki>**</nowiki>missing code
 
=== 13. Daily operation ===
Run these commands inside the folder:<syntaxhighlight lang="bash">
cd /opt/atlas-ha
</syntaxhighlight>
{| class="wikitable"
!Action
!Command
|-
|View logs
|sudo docker logs -f homeassistant
|-
|View status
|sudo docker ps
|-
|Restart
|sudo docker compose restart
|-
|Stop
|sudo docker compose stop
|-
|Start
|sudo docker compose start
|-
|Update
|sudo docker compose pull && sudo docker compose up -d
|}
 
=== 14. Configuration backup ===
The <code>ha-config</code> folder contains the Home Assistant state/configuration. Create a backup:<syntaxhighlight lang="bash">
sudo tar czf ~/atlas-ha-backup.tar.gz -C /opt/atlas-ha docker-compose.yml ha-config
</syntaxhighlight>
 
=== 15. Production notes ===
Before sending a unit to a client, review the following points:
{| class="wikitable"
!Point
!Note
|-
|MQTT Broker
|Mosquitto may be unauthenticated and listening on 0.0.0.0; secure before exposing the unit
|-
|Grafana
|Change default credentials, e.g., admin/admin
|-
|Access Point
|If ATLAS routes traffic via wlan0, validate FORWARD, DOCKER-USER, nftables rules and persistence
|-
|Initial HA account
|The admin account should not be the same on all units
|-
|Network
|Validate port exposure when the unit has a routable IP via Ethernet/LTE
|}
 
=== 16. Automatic installation with script ===
The current installation can be performed using the script:<syntaxhighlight lang="bash">
install-atlas-ha.sh
</syntaxhighlight>This script takes a backup <code>.tar.gz</code> file as an argument and installs/restores Home Assistant in:<syntaxhighlight lang="bash">
/opt/atlas-ha
</syntaxhighlight>
 
==== 16.1 Script used ====
<syntaxhighlight lang="bash">
#!/bin/bash
set -e
 
BACKUP_FILE="$1"
INSTALL_DIR="/opt/atlas-ha"
 
if [ -z "$BACKUP_FILE" ]; then
  echo "Usage:"
  echo "sudo ./install-atlas-ha.sh atlas-ha-backup.tar.gz"
  exit 1
fi
 
echo "Installing dependencies..."
sudo apt update
sudo apt install -y docker.io docker-compose-plugin
 
echo "Enabling Docker..."
sudo systemctl enable docker
sudo systemctl start docker
 
echo "Creating installation folder..."
sudo mkdir -p "$INSTALL_DIR"
 
echo "Extracting backup..."
sudo tar -xzvf "$BACKUP_FILE" -C "$INSTALL_DIR"
 
echo "Starting Home Assistant..."
cd "$INSTALL_DIR"
sudo docker compose up -d
 
echo "Installation complete."
echo "Access at: http://DEVICE_IP:8123"
</syntaxhighlight>
 
==== 16.2 What the script does ====
{| class="wikitable"
!Step
!Action
|-
|Validate argument
|Confirms a backup file was specified
|-
|Install dependencies
|Runs apt update and installs docker.io + docker-compose-plugin
|-
|Enable Docker
|Runs systemctl enable docker and systemctl start docker
|-
|Create folder
|Ensures /opt/atlas-ha exists
|-
|Extract backup
|Extracts the .tar.gz to /opt/atlas-ha
|-
|Start HA
|Enters /opt/atlas-ha and runs sudo docker compose up -d
|-
|Show URL
|Indicates access via <nowiki>http://DEVICE_IP:8123</nowiki>
|}
 
==== 16.3 Prepare files ====
Copy to ATLAS:<syntaxhighlight lang="bash">
install-atlas-ha.sh
atlas-ha-backup.tar.gz
</syntaxhighlight>Example with '''scp''' from PC:<syntaxhighlight lang="bash">
scp install-atlas-ha.sh atlas-ha-backup.tar.gz pi@<atlas-ip>:/home/pi/
</syntaxhighlight>On ATLAS:<syntaxhighlight lang="bash">
cd /home/pi
chmod +x install-atlas-ha.sh
</syntaxhighlight>
 
==== 16.4 Run installation ====
<syntaxhighlight lang="bash">
sudo ./install-atlas-ha.sh atlas-ha-backup.tar.gz
</syntaxhighlight>If the backup has a different name, replace in the command:<syntaxhighlight lang="bash">
sudo ./install-atlas-ha.sh <backup-name>.tar.gz
</syntaxhighlight>
 
==== 16.5 Expected backup structure ====
The backup should contain the necessary structure so that, after extraction in <code>/opt/atlas-ha</code>, at least the following exists:
 
/opt/atlas-ha/
 
├── docker-compose.yml
 
└── ha-config/
 
    ├── configuration.yaml
 
    ├── mqtt.yaml
 
    ├── automations.yaml
 
    ├── scripts.yaml
 
    └── scenes.yaml
 
If the backup does not contain <code>docker-compose.yml</code>, the command <code>sudo docker compose up -d</code> will not be able to start Home Assistant.
 
==== 16.6 Validation after the script ====
Check if the container is active:<syntaxhighlight lang="bash">
sudo docker ps
</syntaxhighlight>View logs:<syntaxhighlight lang="bash">
sudo docker logs -f homeassistant
</syntaxhighlight>Check HTTP response:<syntaxhighlight lang="bash">
curl -s -o /dev/null -w "%{http_code}
" http://localhost:8123
</syntaxhighlight>'''Expected result:'''
{| class="wikitable"
!Code
!Meaning
|-
|200
|Home Assistant accessible
|-
|302
|Home Assistant accessible, with redirection
|-
|000 connection refused
|May be normal in the first 1-2 minutes; wait and retry
|}
Access in browser:
 
http://&#x3C;atlas-ip&#x3E;:8123
 
=== 17. Uninstall / revert ===
Stop and remove the container:<syntaxhighlight lang="bash">
cd /opt/atlas-ha
sudo docker compose down
</syntaxhighlight>Remove the image:<syntaxhighlight lang="bash">
sudo docker image rm ghcr.io/home-assistant/home-assistant:stable
</syntaxhighlight>Optionally, also remove the configuration:<syntaxhighlight lang="bash">
sudo rm -rf /opt/atlas-ha
</syntaxhighlight>The native ATLAS stack, including Node-RED, Grafana, InfluxDB, and Mosquitto, is not removed by these steps.
 
=== 18. Summary ===
{| class="wikitable"
!Step
!Command / Action
|-
|Create folder
|sudo mkdir -p /opt/atlas-ha
|-
|Create compose
|docker-compose.yml with network_mode: host
|-
|Start
|sudo docker compose up -d
|-
|Access
|[http://&#x3C;ip-do-atlas&#x3E;:8123 http://<atlas-ip>:8123]
|-
|Configure MQTT
|Broker 127.0.0.1:1883
|-
|Create entities
|configuration.yaml + mqtt.yaml
|-
|Restart
|sudo docker compose restart
|-
|Logs
|sudo docker logs -f homeassistant
|-
|Backup
|'''tar''' from the '''ha-config''' folder
|-
|Revert
|sudo docker compose down
|}
 
=== 19. Final checklist ===
 
* Docker installed and functional
* homeassistant container in Up state
* Port 8123 accessible
* MQTT configured in Home Assistant
* configuration.yaml file created with <code>mqtt: !include mqtt.yaml</code>
* mqtt.yaml file created
* AI1..AI4 entities created
* DI1..DI4 entities created
* DO1..DO4 entities created
* Node-RED still accessible on 1880
* Mosquitto still accessible on 1883
* Grafana still accessible on 3000
* InfluxDB still accessible on 8086
* Backup of the ha-config folder considered
 
atlas-ha-install-v1.tar.gz