Ported over Pi-Hole On/Off Toggle

Just ported over this. Allow me to quickly disable pi-hole ads blocking from the dashboard. Also can use RM to setup rules of when to enable/disable ads/porn blocking.

Thought it might be useful for others as well on HE.

/**
 *  Pi-hole
 *
 *  Copyright 2018 Nick Veenstra
 *  Convert to Hubitat by cuboy29
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
metadata {
	definition (name: "Pi-hole", namespace: "CopyCat73", author: "Nick Veenstra") {
		capability "Switch"
		capability "Refresh"
        
        command "poll"
        command "refresh"

		attribute "lastupdate", "string"
	}
   
    preferences {
	    section ("Settings") {
            input name: "deviceIP", type:"text", title:"Pi-home IP address", required: true
            input name: "apiToken", type: "text", title: "API token", required: true      
         }
	}

}

private getPort() {
    return 80
}

private getApiPath() { 
	"/admin/api.php" 
}

def installed() {
	log.debug "Installed with settings: ${settings}"
}

def uninstalled() {
	log.debug "uninstalled()"
}

def updated() {
	log.debug "Updated with settings: ${settings}"

	initialize()
}

def initialize() {
	
    // Do the initial poll
	poll()
	// Schedule it to run every minute
	runEvery5Minutes("poll")

}
def refresh() {
	poll()
}

def poll() {

	if (deviceIP == null) {
    	log.debug "IP address missing in preferences"
        return
    }
    def hosthex = convertIPtoHex(deviceIP).toUpperCase()
    def porthex = convertPortToHex(getPort()).toUpperCase()
    def path = getApiPath()
    device.deviceNetworkId = "$hosthex:$porthex" 
  	def hostAddress = "$deviceIP:$port"
    def headers = [:] 
    headers.put("HOST", hostAddress)

    def hubAction = new hubitat.device.HubAction(
        method: "GET",
        path: path,
        headers: headers,
        null,
        [callback : parse] 
    )
    sendHubCommand(hubAction)
}

def parse(response) {
	
    log.debug "Parsing '${response}'"
    def json = response.json
	log.debug "Received '${json}'"
    if (json.FTLnotrunning) {
    	return
    }
    if (json.status == "enabled") {
    	sendEvent(name: "switch", value: "on")
    }
	if (json.status == "disabled") {
    	sendEvent(name: "switch", value: "off")
    }
    if (json.dns_queries_today){
   		if (json.dns_queries_today.toInteger() >= 0) {
    		def combinedValue = "Queries today: " +json.dns_queries_today + " Blocked: " + json.ads_blocked_today + "\nClients: " + json.unique_clients
			sendEvent(name: "combined", value: combinedValue, unit: "")
   		}
    }
    
    sendEvent(name: 'lastupdate', value: lastUpdated(now()), unit: "")
   
}

def on() {
	doSwitch("enable")
}

def off() {
	doSwitch("disable")
}

def doSwitch(toggle) {
	
    if (deviceIP == null) {
    	log.debug "IP address missing in preferences"
        return
    }
    def hosthex = convertIPtoHex(deviceIP).toUpperCase()
    def porthex = convertPortToHex(getPort()).toUpperCase()
    def path = getApiPath() + "?" + toggle + "&auth=" + apiToken
    device.deviceNetworkId = "$hosthex:$porthex" 
  	def hostAddress = "$deviceIP:$port"
    def headers = [:] 
    headers.put("HOST", hostAddress)

    def hubAction = new hubitat.device.HubAction(
        method: "GET",
        path: path,
        headers: headers,
        null,
        [callback : parse] 
    )
    sendHubCommand(hubAction)
}

def lastUpdated(time) {
	def timeNow = now()
	def lastUpdate = ""
	if(location.timeZone == null) {
    	log.debug "Cannot set update time : location not defined in app"
    }
    else {
   		lastUpdate = new Date(timeNow).format("MMM dd yyyy HH:mm", location.timeZone)
    }
    return lastUpdate
}
private String convertIPtoHex(ipAddress) { 
    String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
    return hex

}

private String convertPortToHex(port) {
	String hexport = port.toString().format( '%04x', port.toInteger() )
    return hexport
}
10 Likes

w00t!!! I'll give this a shot.
Been running Pi-Hole on my new Pi 3 for slightly over a week & lovin' the ad-free experience!

J

Edit: Works like a charm!! Thanks.

1 Like

Thanks! I recently have been moving from ST to HE and finding gems like this is making the cross-over so much easier!

Can you add a preference to turn off debug logging?

1 Like

The driver code is not that large. just comment out the log.debug lines where you feel necessary.

2 Likes

Just curious, is there a way to change this so it only disables PiHole for 30 seconds? In the PiHole UI there is the option to disable for 30 seconds. I would much rather do that then forget that I disabled it.

Nevermind, I did a quick little hack to the driver:

def off() {
	doSwitch("disable")
    runIn(30,on)
}

Don't even need to hack the driver just make a rule to turn it back on after any time you choose with simple lighting. I have mine come back on after an hour but it could just as easily be set to 30 seconds.

2 Likes

Nice! Glad I saw this.. it works great. But its there for the hacking. I like a box in preferences to set or change the default.

input "disableTime", "number", title: "Disable time in minutes.<br>(1..1440; Blank = indefinitely)", required: false, range: "1..1440"

And updated the Off function to pass the value in seconds

def off() {
    doSwitch((disableTime) ? "disable=" + (disableTime * 60) : "disable")
}
1 Like

@cuboy29 Nice work, cheers :+1:

This is fantastic.
Thank you @cuboy29 @harriscd

I've made a few mods including incorporating your suggestions harriscd here (and in my own Github so i can update easier).

This is fantastic, way better than the Homebridge plugin I was using.

/**
 *  Pi-hole Virtual Switch Device Driver
 *
 *  Copyright 2018 Nick Veenstra
 *  Convert to Hubitat by cuboy29
 *  Updated by harriscd Aug '20 & sounderdude Mar '21
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 */
metadata {
    definition (name: "Pi-hole Virtual Switch", namespace: "CopyCat73", author: "Nick Veenstra") {
        capability "Switch"
        capability "Refresh"
        
        command "poll"
        command "refresh"

        attribute "lastupdate", "string"
    }
   
    preferences {
        section ("Settings") {
            input name: "deviceIP", type:"text", title:"Pi-home IP address", required: true
            input name: "apiToken", type: "text", title: "API token", required: true
            input "disableTime", "number", title: "Disable time in minutes.<br>(1..1440; Blank = indefinitely)", required: false, range: "1..1440"

         }
    }

}

private getPort() {
    return 80
}

private getApiPath() { 
    "/admin/api.php" 
}

def installed() {
    log.debug "Installed with settings: ${settings}"
}

def uninstalled() {
    log.debug "uninstalled()"
}

def updated() {
    log.debug "Updated with settings: ${settings}"

    initialize()
}
def initialize() {  
    // Do the initial poll
    poll()
    // Schedule it to run every 3 hours
    runEvery3Hours("poll")
    //runEvery5Minutes("poll") 
    //options runEvery__Minutes = 1, 5, 10, 15, 30
    //options runEvery1Hour, runEvery3Hours

}
def refresh() {
    poll()
}

def poll() {

    if (deviceIP == null) {
        log.debug "IP address missing in preferences"
        return
    }
    def hosthex = convertIPtoHex(deviceIP).toUpperCase()
    def porthex = convertPortToHex(getPort()).toUpperCase()
    def path = getApiPath()
    device.deviceNetworkId = "$hosthex:$porthex" 
    def hostAddress = "$deviceIP:$port"
    def headers = [:] 
    headers.put("HOST", hostAddress)

    def hubAction = new hubitat.device.HubAction(
        method: "GET",
        path: path,
        headers: headers,
        null,
        [callback : parse] 
    )
    sendHubCommand(hubAction)
}

def parse(response) {
    
    log.debug "Parsing '${response}'"
    def json = response.json
    log.debug "Received '${json}'"
    if (json.FTLnotrunning) {
        return
    }
    if (json.status == "enabled") {
        sendEvent(name: "switch", value: "on")
    }
    if (json.status == "disabled") {
        sendEvent(name: "switch", value: "off")
    }
    if (json.dns_queries_today){
        if (json.dns_queries_today.toInteger() >= 0) {
            def combinedValue = "Queries today: " +json.dns_queries_today + " Blocked: " + json.ads_blocked_today + "\nClients: " + json.unique_clients
            sendEvent(name: "combined", value: combinedValue, unit: "")
        }
    }
    
    sendEvent(name: 'lastupdate', value: lastUpdated(now()), unit: "")
   
}

def on() {
    doSwitch("enable")
}

/*
def off() {
    doSwitch("disable")
}*/

def off() {
    // Empty will remain off. Any value will be a countdown in minutes to turn back on
    doSwitch((disableTime) ? "disable=" + (disableTime * 60) : "disable")
}

def doSwitch(toggle) {
    
    if (deviceIP == null) {
        log.debug "IP address missing in preferences"
        return
    }
    def hosthex = convertIPtoHex(deviceIP).toUpperCase()
    def porthex = convertPortToHex(getPort()).toUpperCase()
    def path = getApiPath() + "?" + toggle + "&auth=" + apiToken
    device.deviceNetworkId = "$hosthex:$porthex" 
    def hostAddress = "$deviceIP:$port"
    def headers = [:] 
    headers.put("HOST", hostAddress)

    def hubAction = new hubitat.device.HubAction(
        method: "GET",
        path: path,
        headers: headers,
        null,
        [callback : parse] 
    )
    sendHubCommand(hubAction)
}

def lastUpdated(time) {
    def timeNow = now()
    def lastUpdate = ""
    if(location.timeZone == null) {
        log.debug "Cannot set update time : location not defined in app"
    }
    else {
        lastUpdate = new Date(timeNow).format("MMM dd yyyy HH:mm", location.timeZone)
    }
    return lastUpdate
}
private String convertIPtoHex(ipAddress) { 
    String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
    return hex

}

private String convertPortToHex(port) {
    String hexport = port.toString().format( '%04x', port.toInteger() )
    return hexport
}
7 Likes

FWIW, there are also two Node-RED integrations:

and

I use the latter - because I run Pi-Hole on the same computer as Node-RED, and it works flawlessly.

3 Likes

Nice work :wink:

1 Like

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.