Having restarted our Home Assistant Green for maintenance I was somewhat disappointed to see that it apparently doesn’t retry mounting its network shares on boot-up if they fail. Since I’d like our setup to be power-outage safe as much as possible, I had to investigate.
My first attempt was to just create an automation to query the entity state of each network mount and asking it to remount the share if it had failed. Unfortunately, not only doesn’t it auto-remount, there also is no way to even track the mount state.
The linked article also highlights some (many) other limitations of the current
Home Assistant OS network share system that weren’t relevant to me, but
along with an old Home Assistant form post
pointed me in the right direction at least: When interacting with the system
layer in Home Assistant few things are impossible when using the
shell_command
integration which allows running arbitrary commands inside the Home Assistant
Core docker container as root!
Unlike either of these articles, I instead choose to use the native Home Assistant Supervisor API for managing mount points to query the mount states and issues repeated mount attempts. This is what the “System” section of the Home Assistant WebUI also ends up configuring and means that all mount actions will be performed in exactly the same way as the web interface would perform them.
Solution
Defining shell_commands in configuration.yaml
Setting up the shell_command integration is (intentionally) not possible
using the web interface as it is a very advanced feature, so you need to
instead open the configuration.yaml file that is exported inside the config
network share by the Samba share app (or the config/configuration.yaml file
accessible using the File editor app).
Once you’ve located that file add the following content, updating and extending
the list of mount_retry_* commands as indicated in the comment:
# Workaround commands to check for and retry network mounts
shell_command:
# Command to allow reading the current mount state from the Supervisor
mount_query: 'curl -s --oauth2-bearer "$SUPERVISOR_TOKEN" http://supervisor/mounts'
# One command per mount that should be remountable
#
# The name after `mount_retry_` msut be the lower-cased version of the mount
# name visible in the web interface; the name after http://supervisor/mounts/
# must exactly match the casing of the mount in the web interface.
mount_retry_nas: 'curl -s --oauth2-bearer "$SUPERVISOR_TOKEN" -X POST http://supervisor/mounts/NAS/reload'
mount_retry_nas_backup: 'curl -s --oauth2-bearer "$SUPERVISOR_TOKEN" -X POST http://supervisor/mounts/NAS_Backup/reload'
Now go to Settings → [Menu] → Restart Home Assistant → Restart Home Assistant in the web interface and confirm to apply these changes and have the newly defined commands available.
In theory it would be possible to use a template to avoid repeating those
mount_retry_* entries in the configuration file, but:
- There is no direct way to pass parameters to a shell command, only to
substitute the state of a defined entity
- This could be worked around by defining a hidden text helper entity in
the web interface and then updating its state before each invocation of
the given
shell_commandbut it isn’t very intutive to say the least.
- This could be worked around by defining a hidden text helper entity in
the web interface and then updating its state before each invocation of
the given
- Using any templates (or newlines for that matter 🙄) in the
shell_commanddefinition triggers secure environment mode which doesn’t allow any form of shell substition, however we require$SUPERVISOR_TOKENto be substituted with the access token from the Home Assistant Core or any and all requests to Supervisor from our script will be rejected with a permission error response- This could be worked around by wrapping the whole command in
sh -c '…'.
- This could be worked around by wrapping the whole command in
Adding an automation
Using the shell_commands defined above we can now create an automation as
originally intended! Go to Settings → Automations & scenes →
Create automation → Create new automation, then click the menu button and
select Edit in YAML and copy and paste the following:
alias: Retry failed mounts
description: ""
triggers:
- trigger: time_pattern
minutes: "*"
hours: "*"
seconds: "0"
conditions: []
actions:
- alias: Query mount state
action: shell_command.mount_query
metadata: {}
data: {}
response_variable: mount_query
- variables:
mount_states: |
{{ mount_query.stdout | from_json }}
- alias: Repeat for each mount
repeat:
for_each: |
{{ mount_states["data"]["mounts"] }}
sequence:
- if:
- condition: template
value_template: "{{ repeat.item[\"state\"] != \"active\" }}"
alias: If mount state is not active
then:
- action: |
shell_command.mount_retry_{{ repeat.item["name"] | lower }}
data: {}
mode: single
This automation triggers every minute and…:
- Uses the first
shell_commandto query the current state of all mounts from the supervisor - Parses the output of the previous step as JSON object
- Iterates of every returned mount point, and …
- For each mount point checks whether the mount point is active, and …
- If the mount point is not active invokes the defined other
shell_commandbased on the name of the broken mount point- Note that the automation will abort if you forgot to define a
shell_commandfor a failed mount above!
- Note that the automation will abort if you forgot to define a
This means the automation will probe the mount states every minute, but will only attempt to “fix” a mount point if it has actually failed.