Shutdown Home Assistant OS HAOS on Raspberry from within an Addon

Hi,

I have a UPS and I build an AddOn to shutdown gracefully HAOS, when the power fails. But so far it’s only me faileing.

I tried to call the Supervisor with :

curl -X POST \
  -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
  -H "Content-Type: application/json" \
  http://supervisor/host/shutdown

But I get:

403: Forbidden

But I get that for all calls:

local-sh-rpi-daemon:/app# curl -X POST \
  -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
  -H "Content-Type: application/json" \
  http://supervisor/host/shutdown
403: Forbiddenlocal-sh-rpi-daemon:/app# 
local-sh-rpi-daemon:/app# curl -X POST   -H "Authorization: Bearer ${SUPERVISOR_TOKEN}"   -H "Content-Type: application/json"   http://supervisor/core/stop
403: Forbiddenlocal-sh-rpi-daemon:/app# 
local-sh-rpi-daemon:/app# #
local-sh-rpi-daemon:/app# curl -sSL -H "Authorization: Bearer $SUPERVISOR_TOKEN" http://supervisor/network/info
403: Forbiddenlocal-sh-rpi-daemon:/app# 
local-sh-rpi-daemon:/app# 

I tried a script, but that fails as well, even stranger:

local-sh-rpi-daemon:/app# cat /custom-poweroff.sh 
#!/usr/bin/with-contenv bashio
set -e

bashio::log.info "Called to power off Home Assistant!!!"

bashio::host.shutdown()local-sh-rpi-daemon:/app# ^C
local-sh-rpi-daemon:/app# /custom-poweroff.sh 
[03:34:17] INFO: Called to power off Home Assistant!!!
/custom-poweroff.sh: line 7: syntax error: unexpected end of file
local-sh-rpi-daemon:/app# 

Here is my config.yaml ha_addon_sh_rpi_daemon/shrpi-daemon-container/config.yaml at e5f667ce641d341805b2d4c37a451b6acb2c5130 · eburi/ha_addon_sh_rpi_daemon · GitHub

And here is my Dockerfile and all the rest ha_addon_sh_rpi_daemon/shrpi-daemon-container/Dockerfile at e5f667ce641d341805b2d4c37a451b6acb2c5130 · eburi/ha_addon_sh_rpi_daemon · GitHub

Here is the output from the AddOn log when the deamon running inside call the custom-poweroff.sh:

s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
[03:38:15] INFO: Starting Sailor HAT deamon using /usr/bin/shrpid....
I2C devices:
--------------------------------
/dev/i2c-14
/dev/i2c-13
/dev/i2c-1
/dev/i2c-0
--------------------------------
2025-12-01 03:38:15.927 | DEBUG    | shrpi.daemon:parse_arguments:101 - args: Namespace(i2c_bus=1, i2c_addr=109, blackout_time_limit=3.0, blackout_voltage_limit=9.0, socket=None, socket_group='adm', n=False, poweroff='/custom-poweroff.sh', conf=None)
2025-12-01 03:38:15.934 | INFO     | shrpi.daemon:async_main:125 - SH-RPi device detected; HW version 2.0.1, FW version 2.0.6
2025-12-01 03:38:15.934 | INFO     | shrpi.daemon:async_main:182 - Starting shrpid version 2.2.6 on /var/run/shrpid.sock
2025-12-01 03:42:14.768 | WARNING  | shrpi.state_machine:run_state_machine:32 - Detected blackout
2025-12-01 03:42:17.799 | WARNING  | shrpi.state_machine:run_state_machine:41 - Blacked out for 3.0 s, shutting down
2025-12-01 03:42:17.901 | INFO     | shrpi.state_machine:run_state_machine:51 - Executing /custom-poweroff.sh
Traceback (most recent call last):
  File "/usr/bin/shrpid", line 8, in <module>
    sys.exit(daemon())
             ^^^^^^^^
  File "/usr/lib/python3.12/site-packages/shrpi/__main__.py", line 6, in daemon
    shrpi.daemon.main()
  File "/usr/lib/python3.12/site-packages/shrpi/daemon.py", line 202, in main
    asyncio.run(async_main())
  File "/usr/lib/python3.12/asyncio/runners.py", line 195, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/shrpi/daemon.py", line 198, in async_main
    await asyncio.gather(coro1, coro2, coro3)
  File "/usr/lib/python3.12/site-packages/shrpi/state_machine.py", line 52, in run_state_machine
    check_call(["sudo", poweroff])
  File "/usr/lib/python3.12/subprocess.py", line 408, in check_call
    retcode = call(*popenargs, **kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/subprocess.py", line 389, in call
    with Popen(*popenargs, **kwargs) as p:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/subprocess.py", line 1026, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.12/subprocess.py", line 1955, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'sudo'
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped

Automation’s have the option to Perform action

Powers off the host system.

Surely your UPS has a notification to trigger an automation.

Or am I making this too simple for your needs :slight_smile:

Yes, Automations can do this. I could send a message from the AddOn to Mqtt that would indicate the “blackout” and then use an Automation to power off the system. I just thought it would be nice if the AddOn can be self contained and do the complete job and not require an extra automation to be added.

Thinking of it, I could do a complete Mqtt notification including a config message for Mqtt Discovery and therefore get a binary-switch into HomeAssistant that I could use in an automation. That would make it rather easy to use.

But it looks like I’m doing something wrong, as none of the Supervisor calls work. Therefore I think it’s rather a basic mistake I made with the configuration of my AddOn.

Maybe I am missing something, but why go throught the trouble of creating an addon when you have the shutdown action: hassio.host_shutdown and the NUT integration (and addon)?

The problem is, that I don’t have a network UPS. I have this thing right on the Raspberry Pi: Sailor HAT Getting Started | Sailor Hat for Raspberry Pi

The communication between the Raspberry and that HAT is through I2C. Nut does not support that. But thanks for the links. NUT has the option to shutdown the system and if NUT can do it, my AddOn should be able to do it.

The NUT service is meant to connect to the UPS, it does not need to be a networked UPS. Mine wasn’t eiter - it was connected through USB to my NAS, were a NUT server ran. The integration connects to that. The NUT server addon is meant to do the same afaik without a NAS. It is basically a standardized version the addon you are trying to create.

So you suggest that I try to get my needs integrated into NUT?
I need to have access to the native I2C device and I need to run a specific deamon process to monitor that I2C device to find out when the power is failing.

I don’t think it’s practical to put such a specific hardware support into NUT.

Thanks to some hints taken from the NUT AddOn, my AddOn is now working:

Thanks all for the support and suggestions.