Pi-hole automation in Homeassistant

Can I use header authentication with the v6 API? I can't get it to work with Homeassistant. I can authenticate with the code below but I think it's logging out or closing the session because if I run the 2nd command right afterwards it says unauthorized. The 3rd command with the header authentication also says unauthorized.

rest_command:
  pihole_authenticate:
    url: http://192.168.0.1:8080/api/auth
    method: POST
    payload: '{ "password" : "api_key" }'
    content_type:  'application/json; charset=utf-8'
    verify_ssl: false
  pihole_disable_5_min:
    url: http://192.168.0.1:8080/api/dns/blocking
    method: POST
    payload: '{ "blocking" : "false", "timer" : "300" }'
    content_type:  'application/json; charset=utf-8'
    verify_ssl: false
  pihole_disable:
    url: http://192.168.0.1:8080/api/dns/blocking
    method: POST
    headers:
      authorization: "Bearer api_key"
      accept: "application/json, text/html"
      user-agent: 'Mozilla/5.0 {{ useragent }}'
    payload: '{ "blocking" : "false", "timer" : "300" }'
    content_type:  'application/json; charset=utf-8'
    verify_ssl: false

Neither, nor, actually. What you obtain from the first endpoint is a session ID (sid) that needs to be included in following requests to verify you have an authenticated session. How should Pi-hole know you're authenticated otherwise?

I guess you'd need something like this: RESTful command - Using REST command Response in automations :: Home Assistant

Check the return code of the first endpoint and then extract the obtained from the response_variable, store it in a new variable and use it within the payload of the following commands.

Maybe like this:

automation:
  - alias: "Disable Pi-hole for 5 minutes"
    triggers:
      - trigger: event
        event_type: pi_hole_disable_5_min
    actions:
      - action: rest_command.pihole_authenticate
        response_variable: response
      - if: "{{ response['status'] == 200 }}"
        then:
          - alias: "Parse data"
            variables:
              sid: "{{ response['content']['session']['sid'] }}"
          - action: rest_command.pihole_disable_5_min
            data:
              sid: "{{ sid }}"
          - service: rest_command.pihole_logout
            data:
              sid: "{{ sid }}"
        else:
          - service: persistent_notification.create
            data:
              message: "Failed to authenticate with Pi-hole API"
              title: "Pi-hole API Error"

rest_command:
  pihole_authenticate:
    url: http://192.168.2.11/api/auth
    method: POST
    payload: '{ "password" : "your_password" }'
    content_type:  'application/json; charset=utf-8'
    verify_ssl: false
  pihole_logout:
    url: http://192.168.2.11/api/auth
    method: DELETE
    verify_ssl: false
    headers:
      sid: "{{ sid }}"
  pihole_disable_5_min:
    url: http://192.168.2.11/api/dns/blocking
    method: POST
    payload: '{ "blocking" : false, "timer" : 300 }'
    content_type:  'application/json; charset=utf-8'
    verify_ssl: false
    headers:
      sid: "{{ sid }}"

It needs to be added to your configuration.yaml

Thanks! I didn't know you were working on this. I've spend about 5 hours on it so far and got it working with something similar. I'll post it here just to show how I put the sid in the URI instead. I will implement your code as I was going to try to figure out how to log out but you have the code done already. I added code for the switch you can add to your dashboard.

configuration.yaml

rest_command:
  pihole_authenticate:
    url: http://192.168.7.1:8080/api/auth
    method: POST
    payload: '{ "password" : your_app_password" }'
    content_type:  'application/json; charset=utf-8'
    verify_ssl: false
  pihole_disable_5_min:
    url: "http://192.168.7.1:8080/api/dns/blocking?sid={{ pihole_sid }}"
    method: POST
    payload: '{ "blocking":false, "timer":300 }'
    content_type:  'application/json; charset=utf-8'
    verify_ssl: false

switch:
 - platform: template
   switches:
    pihole_disable_5_min:
      unique_id: "my_uuid"
      value_template: "{{ is_state('automation.pi_hole_disable_for_5_minutes', 'on') }}"
      turn_on:
        service: automation.trigger
        target:
          entity_id: automation.pi_hole_disable_for_5_minutes
      turn_off:
        service: automation.trigger
        target:
          entity_id: automation.pi_hole_disable_for_5_minutes

automation.yaml

- id: '1734288492632'
  alias: Pi-hole - Disable for 5 minutes
  description: ''
  triggers: []
  conditions: []
  actions:
  - action: rest_command.pihole_authenticate
    response_variable: pihole_response
    data: {}
  - action: rest_command.pihole_disable_5_min
    data:
      pihole_sid: '{{ pihole_response[''content''][''session''][''sid''] }}'
  mode: single

No worries, another, possibly cleaner solution would be to realize the entire enable/disable actions via a (re-usable) script. I did just out this together here:

Check out here:

https://deploy-preview-1143--pihole-docs.netlify.app/guides/misc/homeassistant/

3 Likes

Wow, great job.

When I was creating the switch. I was thinking about how to make a sensor to read when the blocking is enabled again and turn the switch back to the off position. Just brainstorming here, I think it would be possible to create a sensor if you had an automation acquire a new sid right before the session times out and store it in an input text.

1 Like

Yeah, you'd need to query the blocking status sufficiently often to get this right. I think adding more and more complicated automation isn't going to be a good solution, better would be creating a Home Assistant integration like core/homeassistant/components/pi_hole at dev · home-assistant/core · GitHub but for Pi-hole v6. Using an integration is a lot easier as the interface would be cleaner but you could also write only a tiny little bit of Python code that ensures you always have a valid SID around and don't need to do the login/logout each time.

Sure, on a sufficiently "beefy" Pi-hole, login/logout is no issue, however, if you are using a low-end device, checking the password may take a noticeable (even if still small) delay. This is due to the much improved Balloon password algorithm making hacking your password practically impossible*.

*) On Pi-hole v5 and before, the algorithm was already impossible to reverse (a so called "one-way function") but the possibility to compute it in light speed makes it possible to brute force the password given sufficient compute power is available. While still not really feasible, it'd theoretically be possible. With v6, it is not practically possible any longer. No known quantum algorithms exist to solve this faster, either.

3 Likes