Webiopi

I have been thinking, would it be possible to control the gpio's of a raspberry pi to ultimately switch some lv relays. I came across this on my search...

http://webiopi.trouch.com

I really not a coder, could this interface with HE, if so how hard would it be?

I have it working on HE.
Manager app:

/**
 *  WebOIPi Manager
 *
 *  Copyright 2016 iBeech
 *
 *  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.
 *
 * 	==== INSTRUCTIONS ===
	1) For UK go to: https://graph-eu01-euwest1.api.smartthings.com
	2) For US go to: https://graph.api.smartthings.com
	3) Click 'My SmartApps'
	4) Click the 'From Code' tab
	5) Paste in the code from here, into SmartThings
	6) Click 'Create'
	7) Click 'Publish -> For Me'

 * 
 */
definition(
    name: "WebOIPi Manager",
    namespace: "ryancasler",
    author: "rcasler",
    description: "Add each Pi relay, contact or tempt sensor as a device in Hubitat.",
    category: "Safety & Security",
    iconUrl: "http://download.easyicon.net/png/1179528/64/",
    iconX2Url: "http://download.easyicon.net/png/1179528/128/",
    iconX3Url: "http://download.easyicon.net/png/1179528/128/")


preferences {

  section("Raspberry Pi Setup"){
  	input "piIP", "text", "title": "Raspberry Pi IP", multiple: false, required: true
    input "piPort", "number", "title": "Raspberry Pi Port", multiple: false, required: true
    input "piPolling", "number", "title": "Polling time for Switchs & Contacts sensors in seconds", multiple: false, required: true
    input "piUser","text", "title": "User", multiple: false, required: false
    input "piPsswd","text", "title": "Password", multiple: false, required: false
      input (
          name: "configLoggingLevelIDE",
          title: "IDE Live Logging Level:\nMessages with this level and higher will be logged to the IDE.",
          type: "enum",
          options: [
              "0" : "None",
              "1" : "Error",
              "2" : "Warning",
              "3" : "Info",
              "4" : "Debug",
              "5" : "Trace"
          ],
          defaultValue: "5",
          displayDuringSetup: true,
          required: false
      )
  }
  
    section("Device 1") {    
		input "deviceName1", "text", title: "Device Name", required:false	        
        input "deviceType1", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig1", "text", title: "GPIO# or Device Name", required: false
    }
    section("Device 2") {    
		input "deviceName2", "text", title: "Device Name", required:false	        
        input "deviceType2", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig2", "text", title: "GPIO# or Device Name", required: false
    }  
    section("Device 3") {    
		input "deviceName3", "text", title: "Device Name", required:false	        
        input "deviceType3", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig3", "text", title: "GPIO# or Device Name", required: false
    }      
    section("Device 4") {    
		input "deviceName4", "text", title: "Device Name", required:false	        
        input "deviceType4", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig4", "text", title: "GPIO# or Device Name", required: false
    }          
    section("Device 5") {    
		input "deviceName5", "text", title: "Device Name", required:false	        
        input "deviceType5", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig5", "text", title: "GPIO# or Device Name", required: false
    }    
    section("Device 6") {    
		input "deviceName6", "text", title: "Device Name", required:false	        
        input "deviceType6", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig6", "text", title: "GPIO# or Device Name", required: false
    }    
    section("Device 7") {    
		input "deviceName7", "text", title: "Device Name", required:false	        
        input "deviceType7", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig7", "text", title: "GPIO# or Device Name", required: false
    }   
    section("Device 8") {    
		input "deviceName8", "text", title: "Device Name", required:false	        
        input "deviceType8", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig8", "text", title: "GPIO# or Device Name", required: false
    }        
    section("Device 9") {    
		input "deviceName9", "text", title: "Device Name", required:false	        
        input "deviceType9", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig9", "text", title: "GPIO# or Device Name", required: false
    }        
    section("Device 10") {    
		input "deviceName10", "text", title: "Device Name", required:false	        
        input "deviceType10", "enum", title: "Device Type", required: false, options: [                
                "switch":"Relay Switch",
                "contact":"Contact Sensor",
            	"motion":"Motion Sensor",
                "temperatureSensor":"Temperature Sensor"]
        input "deviceConfig10", "text", title: "GPIO# or Device Name", required: false
    }  
}

def installed() {
  logger("Installed with settings: ${settings}","debug")

  initialize()
}

def initialize() {
    // Update internal state:
    state.loggingLevelIDE = (settings.configLoggingLevelIDE) ? settings.configLoggingLevelIDE.toInteger() : 5

    subscribe(location, null, response, [filterEvents:false])
    
    unschedule()
    runEvery1Minute(Schedule_updateGPIOState)
    Schedule_updateGPIOState()
   
	setupVirtualRelay(deviceName1, deviceType1, deviceConfig1);
    setupVirtualRelay(deviceName2, deviceType2, deviceConfig2);
    setupVirtualRelay(deviceName3, deviceType3, deviceConfig3);
    setupVirtualRelay(deviceName4, deviceType4, deviceConfig4);
    setupVirtualRelay(deviceName5, deviceType5, deviceConfig5);
    setupVirtualRelay(deviceName6, deviceType6, deviceConfig6);
    setupVirtualRelay(deviceName7, deviceType7, deviceConfig7);
    setupVirtualRelay(deviceName8, deviceType8, deviceConfig8);
    setupVirtualRelay(deviceName9, deviceType9, deviceConfig9);
    setupVirtualRelay(deviceName10, deviceType10, deviceConfig10);
}

def updated() {
log.info "loggingLevelIDE:$state.loggingLevelIDE"
    // Update internal state:
    state.loggingLevelIDE = (settings.configLoggingLevelIDE) ? settings.configLoggingLevelIDE.toInteger() : 5
log.info "loggingLevelIDE:$state.loggingLevelIDE"
    
	logger("Updated with settings: ${settings}","debug")
    unschedule()
    runEvery1Minute(Schedule_updateGPIOState)
    Schedule_updateGPIOState()
    unsubscribe();
    
    updateVirtualRelay(deviceName1, deviceType1, deviceConfig1);
    updateVirtualRelay(deviceName2, deviceType2, deviceConfig2);    
    updateVirtualRelay(deviceName3, deviceType3, deviceConfig3);
    updateVirtualRelay(deviceName4, deviceType4, deviceConfig4);
    updateVirtualRelay(deviceName5, deviceType5, deviceConfig5);
    updateVirtualRelay(deviceName6, deviceType6, deviceConfig6);
    updateVirtualRelay(deviceName7, deviceType7, deviceConfig7);
    updateVirtualRelay(deviceName8, deviceType8, deviceConfig8);
    updateVirtualRelay(deviceName9, deviceType9, deviceConfig9);
    updateVirtualRelay(deviceName10, deviceType10, deviceConfig10);
    
    subscribe(location, null, response, [filterEvents:false])
}

def updateVirtualRelay(deviceName, deviceType, deviceConfig) {
    
    // If user didn't fill this device out, skip it
    if(!deviceName) return;
    
    def theDeviceNetworkId = "";
    switch(deviceType) {
    	case "switch":
        	theDeviceNetworkId = getRelayID(deviceConfig);
            break;
            
    	case "contact":
        	theDeviceNetworkId = getContactID(deviceConfig);
            break;
            
        case "temperatureSensor":
        	theDeviceNetworkId = getTemperatureID(deviceConfig);
            break;
    }
    
    logger("Searching for: $theDeviceNetworkId","trace")
    
  	def theDevice = getChildDevices().find{ d -> d.deviceNetworkId.startsWith(theDeviceNetworkId) }  
    
    if(theDevice){ // The switch already exists
		logger("Found existing device which we will now update"   ,"debug")
        theDevice.deviceNetworkId = theDeviceNetworkId //+ "." + deviceConfig
        theDevice.label = deviceName
        theDevice.name = deviceName
        
        if(deviceType == "switch") { // Actions specific for the relay device type
            subscribe(theDevice, "switch", switchChange)
			logger("Setting initial state of $deviceName to off","debug")
            setSwitchState(deviceConfig, "off", true);
            theDevice.off();
        } else if(deviceType == "contact") { // Actions specific for the contact device type
            subscribe(theDevice, "contact", contactChange)
			logger("Setting initial state of $deviceName to open","debug")
            setContactState(deviceConfig, "open", true);
            theDevice.open();
        } else {
        	updateTempratureSensor();
        }
        
    } else { // The switch does not exist
    	if(deviceName){ // The user filled in data about this switch
			logger("This device does not exist, creating a new one now","debug")
        	/*setupVirtualRelay(deviceId, gpioName);*/
            setupVirtualRelay(deviceName, deviceType, deviceConfig);
       	}
    }

}
def setupVirtualRelay(deviceName, deviceType, deviceConfig) {

	if(deviceName){
		logger(deviceName,"debug")
		logger(deviceType,"debug")
		logger(deviceConfig,"debug")
        
        switch(deviceType) {
        	case "switch":
				logger("Found a relay switch called $deviceName on GPIO #$deviceConfig","trace")
				def d = addChildDevice("ryancasler", "Virtual Pi Relay", getRelayID(deviceConfig), null, [label:deviceName, name:deviceName])
	    		subscribe(d, "switch", switchChange)
                logger("Setting initial state of $gpioName to off","debug")
        		setSwitchState(deviceConfig, "off", true);
	    		d.off();
            	break;
                    
        	case "contact":
                logger("Found a Contact contact called $deviceName on GPIO #$deviceConfig","trace")
				def d = addChildDevice("ryancasler", "Virtual Pi Contact", getContactID(deviceConfig), null, [label:deviceName, name:deviceName])
	    		subscribe(d, "contact", contactChange)
                
                logger("Setting initial state of $gpioName to open","debug")
        		setContactState(deviceConfig, "open", true);
	    		d.open();
            	break;
                    
            case "temperatureSensor":
                logger("Found a temperature sensor called $deviceName on $deviceConfig","trace")
                def d = addChildDevice("ryancasler", "Virtual Pi Temperature", getTemperatureID(deviceConfig), null, [label:deviceName, name:deviceName])                 
                state.temperatureZone = deviceConfig
                updateTempratureSensor();
            	break;

            case "motion":
                logger("Found a motion sensor called $deviceName on GPIO #$deviceConfig","trace")
				def d = addChildDevice("ryancasler", "Virtual Pi Motion", getContactID(deviceConfig), null, [label:deviceName, name:deviceName])
	    		subscribe(d, "motion", motionChange)
                
                logger("Setting initial state of $gpioName to open","debug")
        		setMotionState(deviceConfig, "active", true);
	    		d.open();
            	break;
        }	    
	}
}

def String getRelayID(deviceConfig) {

	return "piRelay." + settings.piIP + "." + deviceConfig
}
def String getContactID(deviceConfig) {

	return "piContact." + settings.piIP + "." + deviceConfig
}
def String getTemperatureID(deviceConfig){
    
    return  "piTemp." + settings.piIP + "." + deviceConfig
}

def uninstalled() {
  unsubscribe()
  def delete = getChildDevices()
    delete.each {
    	unsubscribe(it)
		logger("about to delete device","trace")
        deleteChildDevice(it.deviceNetworkId)
    }   
}

def response(evt){
    logger("response","debug")
    def msg = parseLanMessage(evt.description);
    if(msg && msg.body){

            // This is the GPIO headder state message
        def children = getChildDevices(false)
        if(msg.json) {
                msg.json.GPIO.each { item ->
                    updateRelayDevice(item.key, item.value.value, children);
                }
                logger("Finished Getting GPIO State","trace")
        }

        def tempContent = msg.body.tokenize('.')
        if(tempContent.size() == 2 && tempContent[0].isNumber() && tempContent[1].isNumber() ) {
            // Got temperature response            
            def networkId = getTemperatureID(state.temperatureZone);
            def theDevice = getChildDevices().find{ d -> d.deviceNetworkId.startsWith(networkId) }              
            if(theDevice) {
                theDevice.setTemperature(msg.body, state.temperatureZone);
                logger("$theDevice set to $msg.body","trace")
            }
		}
	}
}

def updateRelayDevice(GPIO, state, childDevices) {

	def theDevice = childDevices.find{ d -> d.deviceNetworkId.endsWith(".$GPIO") }  
    if(theDevice) { 
		logger("Updating $theDevice for GPIO $GPIO with value $state","debug")
        theDevice.changeState(state)
    }
}

def updateTempratureSensor() {

	logger("Updating temperature for $state.temperatureZone","info")
	
	executeRequest("/devices/" + state.temperatureZone  + "/sensor/temperature/c", "GET", false, null, true);
    
    //runIn(60, updateTempratureSensor);
}

def Schedule_updateGPIOState() {
	logger("Schedule_updateGPIOState","trace")
    def nbPolling = Math.round(60/piPolling) -1
    def Polling = piPolling
	for (i in 1..nbPolling) {
    	Polling = piPolling * i
        runIn(Polling, updateGPIOState, [overwrite: false]);
	}
    updateTempratureSensor()
}
def updateGPIOState() { 
	logger("updateGPIOState","info")
	def stamp = new Date().format('yyyy-M-d hh:mm:ss',location.timeZone)
	logger("Updating GPIO map at " + stamp,"trace")
	
	executeRequest("/*", "GET", false, null, true);
    
    //runIn(piPolling, updateGPIOState);
}

def switchChange(evt){

	logger("Switch event!","debug")
	logger(evt.value,"debug")
    if(evt.value == "on" || evt.value == "off") return;    
	
    
    def parts = evt.value.tokenize('.');
    def deviceId = parts[1];
    def GPIO = parts[5];
    def state = parts[6];
    
	logger(state,"debug")
    
    switch(state){
    	case "refresh":
        // Refresh this switches button
		logger("Refreshing the state of GPIO " + GPIO,"debug")
        executeRequest("/*", "GET", false, null, true)
        return;        
    }
    
    setSwitchState(GPIO, state, true);
    
    return;
}
def contactChange(evt){

		logger("Contact event!","debug")
		logger(evt.value,"debug")
    if(evt.value == "closed" || evt.value == "open") return;    
	
    
    def parts = evt.value.tokenize('.');
    def deviceId = parts[1];
    def GPIO = parts[5];
    def state = parts[6];
    
	logger(state,"debug")
    
    switch(state){
    	case "refresh":
        // Refresh this switches button
		logger("Refreshing the state of GPIO " + GPIO,"debug")
        executeRequest("/*", "GET", false, null, true)
        return;        
    }
    
    setContactState(GPIO, state, false);
    
    return;
}

def motionChange(evt){

		logger("Contact event!","debug")
		logger(evt.value,"debug")
    if(evt.value == "inactive" || evt.value == "active") return;    
	
    
    def parts = evt.value.tokenize('.');
    def deviceId = parts[1];
    def GPIO = parts[5];
    def state = parts[6];
    
	logger(state,"debug")
    
    switch(state){
    	case "refresh":
        // Refresh this switches button
		logger("Refreshing the state of GPIO " + GPIO,"debug")
        executeRequest("/*", "GET", false, null, true)
        return;        
    }
    
    setContactState(GPIO, state, false);
    
    return;
}
def setSwitchState(gpio, state, PostactualAction) {
	logger("Executing 'setSwitchState'","debug")
     
    // Determine the path to post which will set the switch to the desired state
    def Path = "/GPIO/" + gpio + "/value/";
	Path += (state == "on") ? "1" : "0";
    
    executeRequest(Path, "POST", "OUT", gpio, PostactualAction);
}

def setContactState(gpio, state, PostactualAction) {
	logger("Executing 'setContactState'","debug")

    // Determine the path to post which will set the switch to the desired state
    def Path = "/GPIO/" + gpio + "/value/";
	Path += (state == "on") ? "1" : "0";
    
    executeRequest(Path, "POST", "IN", gpio, PostactualAction);
}

def executeRequest(Path, method, setGPIODirection, gpioPin, PostactualAction) {
		   
	logger("The " + method + " path is: " + Path,"debug")
	    
    def headers = [:] 
    headers.put("HOST", "$settings.piIP:$settings.piPort")
    def decoded="$settings.piUser:$settings.piPsswd"
    def encoded = "Basic "+decoded.bytes.encodeBase64()  
    headers.put("Authorization",encoded)
    
    try {    	
        if(setGPIODirection == "OUT") {
        	def setDirection = new hubitat.device.HubAction(
            	method: "POST",
            	path: "/GPIO/" + gpioPin  + "/function/OUT",
            	headers: headers)
			logger("POSTing OUT","trace")
        	sendHubCommand(setDirection);
        } else if(setGPIODirection == "IN") {
        	def setDirection = new hubitat.device.HubAction(
            	method: "POST",
            	path: "/GPIO/" + gpioPin  + "/function/IN",
            	headers: headers)
				logger("POSTing IN","trace")
        	sendHubCommand(setDirection);
        }
        
        def actualAction = new hubitat.device.HubAction(
            method: method,
            path: Path,
            headers: headers)

        sendHubCommand(actualAction)        
    }
    catch (Exception e) {
		logger("Hit Exception $e on $hubAction","warn")
    }
}

/* Helper functions to get the network device ID */
private String NetworkDeviceId(){
    def iphex = convertIPtoHex(settings.piIP).toUpperCase()
    def porthex = convertPortToHex(settings.piPort)
    return "$iphex:$porthex" 
}

private String convertIPtoHex(ipAddress) { 
    String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
	logger("IP address entered is $ipAddress and the converted hex code is $hex","debug")
    return hex

}

private String convertPortToHex(port) {
    String hexport = port.toString().format( '%04x', port.toInteger() )
	logger("hexport:$hexport","debug")
    return hexport
}
/**
 *  logger()
 *
 *  Wrapper function for all logging.
 **/
private logger(msg, level = "debug") {

    switch(level) {
        case "error":
            if (state.loggingLevelIDE >= 1) log.error msg
            break

        case "warn":
            if (state.loggingLevelIDE >= 2) log.warn msg
            break

        case "info":
            if (state.loggingLevelIDE >= 3) log.info msg
            break

        case "debug":
            if (state.loggingLevelIDE >= 4) log.debug msg
            break

        case "trace":
            if (state.loggingLevelIDE >= 5) log.trace msg
            break

        default:
            log.debug msg
            break
    }
}

Virtual pi relay:

/**
 *  Pi Relay Control
 *
 *  Copyright 2018 Ryan Casler
 *
 *  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: "Virtual Pi Relay", namespace: "ryancasler", author: "rcasler") {
		capability "Switch"
        capability "Refresh"
		capability "Polling"
        
        command "changeState", ["string"]
	}

	simulator {
		// TODO: define status and reply messages here
	}

	tiles {    
        
		 standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
			state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821"
			state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff"
		}
        
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 1, height: 1) {
			state("default", label:'refresh', action:"polling.poll", icon:"st.secondary.refresh-icon")
		}

		main "switch"
		details (["switch", "refresh"])
	}
}

// parse events into attributes
def parse(String description) {
	log.debug "Virtual siwtch parsing '${description}'"
}

def poll() {
	log.debug "Executing 'poll'"   
        
        def lastState = device.currentValue("switch")
    	sendEvent(name: "switch", value: device.deviceNetworkId + ".refresh")
        sendEvent(name: "switch", value: lastState);
}

def refresh() {
	log.debug "Executing 'refresh'"
    
	poll();
}

def on() {
	log.debug "Executing 'on'"	     
    
    sendEvent(name: "switch", value: device.deviceNetworkId + ".on");    
    sendEvent(name: "switch", value: "on");    
}

def off() {
	log.debug "Executing 'off'"
	    
	sendEvent(name: "switch", value: device.deviceNetworkId + ".off");     
    sendEvent(name: "switch", value: "off");
}

def changeState(newState) {

	log.trace "Received update that this switch is now $newState"
	switch(newState) {
    	case 1:
			sendEvent(name: "switch", value: "on")
            break;
    	case 0:
        	sendEvent(name: "switch", value: "off")
            break;
    }
}

Thanks Ryan, I'll look forward to trying this out in the near future

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