SmartThings smartapps, DH's etc

Is it safe to say that all smartapps and DH's from smartthings can be ported across - with a few modifications?

I'm keen for HE, but wary that I wont be able to port a couple of the homebaked (not by me) DH's and child apps that I currently use.

I use Sonos, Xiaomi sensors, an Aussie DH for some smartlights that are specific to Aust, webcore, Google Home, Google Home helper, Bosch motion detectors, and Sony TV integration. I'm also running an Alarm system bridge between my house alarm, and Smartthings. I'll put the DH in the next post, for review. Would it work?

My smartapps are a unifi wifi presence app that I stole from an ASUS router thread and modified.

/**
 *  Copyright 2018 LeeF Automation
 *
 *  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.
 *
 *  Alarm Bridge Controller
 *
 *  Author: LeeF Automation
 *  Date: 2018-05-06
 *
 *  Special thanks to CJCharles who created the Visonic alarm system integration that this device handler is evolved from
 */

import groovy.json.JsonSlurper

metadata
{
	definition (name: "Alarm System Bridge", namespace: "leefauto", author: "LeeF Automation")
	{
		capability "Refresh"
		capability "Configuration"
		capability "Alarm"
		capability "Polling"
		
		attribute "device.status", "string"
		attribute "armaway", "string"
		attribute "armhome", "string"
		attribute "disarm", "string"
		attribute "alarm", "enum"
		attribute "lastaction", "string"
		
		command "AlarmArmAway"
		command "AlarmArmHome"
		command "AlarmDisarm"
		command "AlarmTrigger"
		
		command "ZoneCreateZoneDevices"
		command "ZoneRemoveZoneDevices"
		command "ZoneTestFunction"
		
		command "updateZonesNS"
		command "stop"
		command "off"
		command "strobe"
		command "siren"
		command "both"
	}

	simulator {
	}
	
	preferences {
		input name: "ip", type: "string", title:"Alarm IP Address", description: "e.g. 192.168.1.10", required: true, displayDuringSetup: true
		input name: "prename", type: "string", title:"Add before zone name", description: "e.g. 'Zone' would give 'Zone Kitchen'", required: false, displayDuringSetup: true
		input name: "postname", type: "string", title:"Add after zone name", description: "e.g. 'Zone' would give 'Kitchen Zone'", required: false, displayDuringSetup: true
		input name: "inactivityseconds", type: "string", title:"Motion sensor inactivity timeout", description: "override the default of 20s (60s max)", required: false, displayDuringSetup: false
		input name: "password", type: "password", title:"Password", required:false, displayDuringSetup:false
	}

	tiles (scale: 2)
	{	  
		multiAttributeTile(name:"AlarmStatus", type: "generic", width: 6, height: 3, decoration:"flat")
		{
			tileAttribute ("device.status", key: "PRIMARY_CONTROL")
			{
				attributeState "disarmed", label:'${name}', icon: "st.security.alarm.off", backgroundColor: "#505050"
				attributeState "home", label:'${name}', icon: "st.Home.home4", backgroundColor: "#00BEAC"
				attributeState "away", label:'${name}', icon: "st.security.alarm.on", backgroundColor: "#008CC1"
				attributeState "alarm", label:'${name}', icon: "st.security.alarm.on", backgroundColor: "#d44556"
			}
			tileAttribute("device.events", key: "SECONDARY_CONTROL", wordWrap: true)
			{
				attributeState("default", label:'${currentValue}')
			}
		}
		
		standardTile("AlarmArmAway", "armaway", height: 2, width:2, decoration:"flat", inactiveLabel: false)
		{
			state "inactive", label:"Away", action:"AlarmArmAway", backgroundColor:"#D8D8D8"
			state "changing", label:"Arming Away", action:"", backgroundColor:"#FF9900"
			state "active", label:"Armed Away", action:"", backgroundColor:"#00CC00"
		}
		standardTile("AlarmArmHome", "armhome", height: 2, width: 2, decoration:"flat", inactiveLabel: false)
		{
			state "inactive", label:"Home", action:"AlarmArmHome", backgroundColor:"#D8D8D8"
			state "changing", label:"Arming Home", action:"", backgroundColor:"#FF9900"
			state "active", label:"Armed Home", action:"", backgroundColor:"#00CC00"
		}
		standardTile("AlarmDisarm", "disarm", height: 2, width: 2, decoration:"flat", inactiveLabel: false)
		{
			// disarming via SmartThings is currently disabled due to security concerns
			state "inactive", label:"Disarm", action:"AlarmDisarm", backgroundColor:"#D8D8D8"
			state "inactive", label:"Disarm", action:"", backgroundColor:"#D8D8D8"
			state "changing", label:"Disarming", action:"", backgroundColor:"#FF9900"
			state "active", label:"Disarmed", action:"", backgroundColor:"#00CC00"
		}
		
		//This will create a tile for all zones up to 30 (which should cover most boards including Wired ones)
		//We can have all of these tiles, but not display them
		(1..30).each { n ->
			valueTile("zonename$n", "panelzonename$n", height: 1, width: 2) {
				state "default", label:'${currentValue}', backgroundColor:"#FFFFFF"}
			standardTile("zone$n", "panelzone$n", height: 1, width: 1) {
				state "inactive", label:"Inactive", action:"", icon:""
				state "active", label:"Active", action:"", icon:"", backgroundColor:"#00CC00"
				state "closed", label:"Closed", action:"", icon:""
				state "open", label:"Open", action:"", icon:"", backgroundColor:"#00CC00"
				state "bypass", label:"Bypass", action:"", icon:"", backgroundColor:"#FFD800"
				state "smoke", label:"Smoke", action:"", icon:"", backgroundColor:"#FF3F00"
				state "clear", label:"Clear", action:"", icon:""
				}
		}
		
		standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 1, height: 1)
		{
			state "default", label:"", action:"refresh", icon:"st.secondary.refresh"
		}
	  
		standardTile("configure", "device.configure", inactiveLabel: false, width: 1, height: 1, decoration: "flat")
		{
			state "configure", label:'', action:"configure", icon:"st.secondary.configure"
		}
	   
		standardTile("customzones", "device.zoneupdate", inactiveLabel: false, width: 1, height: 1, decoration: "flat")
		{
			state "configure", label:'Zone Names', action:"updateZonesNS"
		}

		valueTile("ip", "ip", decoration: "flat", width: 2, height: 1)
		{
			state "ip", label:'ST IP Addr:\r\n${currentValue}'
		}
		
		standardTile("createtile", "device.createzonedevices", inactiveLabel: false, decoration: "flat", width: 2, height: 1)
		{
			state "default", label:'Create Zone Devices', action:"ZoneCreateZoneDevices", icon: "st.unknown.zwave.remote-controller"
		}
		standardTile("removetile", "device.removezonedevices", inactiveLabel: false, decoration: "flat", width: 2, height: 1)
		{
			state "default", label:'Remove Zone Devices', action:"ZoneRemoveZoneDevices", icon: "st.samsung.da.washer_ic_cancel"
		}

		standardTile("testtile", "device.testfunction", inactiveLabel: false, decoration: "flat", width: 2, height: 1)
		{
			state "default", label:'test_function', action:"ZoneTestFunction"
		}

		
		// This alarm tile is used so that the state of the alarm is recorded in a useful existing ST capability.
		// This allows you to trigger other things in CoRE (or similar) when the state changes, or to trigger state changes
		// To use this, you can make a CoRE trigger if 'Alarm' state changes to 'Both' (this will tell you the alarm is going off)
		// or if 'Alarm' state changes to 'Off' (this will tell you the alarm has been disarmed).
		// Alternatively if you set the 'Alarm' capability to 'Siren' then it will arm Away.
		standardTile("alarm", "device.alarm", inactiveLabel: false, width: 2, height: 1, decoration: "flat")
		{
			state "off", label:'Alarm state = Off (Disarmed)'
			state "strobe", label:'Alarm state = Strobe (Home)'
			state "siren", label:'Alarm state = Siren (Away)'
			state "both", label:'Alarm state = Both (Alarm)'
		}
	}

	main(["AlarmStatus"])
	details(["AlarmStatus", "AlarmArmAway", "AlarmArmHome", "AlarmDisarm",
			 
			 // Add all your zones here (zonenameX first then zoneX)- e.g. add "zonename29", "zone29" if you want to display zone 29 in the alarm panel
			 "zonename1", "zone1", "zonename2", "zone2", "zonename3", "zone3", "zonename4", "zone4",
			 "zonename5", "zone5", "zonename6", "zone6", "zonename7", "zone7", "zonename8", "zone8", 
			 
			 "refresh", "configure", "ip", "alarm", "createtile", "removetile"
			 , "testtile" //If you have trouble creating your child zones you can uncomment the start of this line (remove // before the comma)
			 				//After pressing the "test_function" button on your phone you can re-comment it, to tidy up your alarm panel device.
							//This is only needed if you have trouble creating your zones.
			 ])
}

def AlarmArmAway()
{
	// Send an arm away command to the alarm and log that it is changing
	log.debug "armaway()"
	sendEvent(name: "armaway", value: "changing")
	sendEvent(name: "armhome", value: "inactive", displayed: false)
	sendEvent(name: "disarm", value: "inactive", displayed: false)
	getAction("/armaway")
}

def AlarmArmHome()
{
	// Send an arm home command to the alarm and log that it is changing
	log.debug "armhome()"
	sendEvent(name: "armaway", value: "inactive", displayed: false)
	sendEvent(name: "armhome", value: "changing")
	sendEvent(name: "disarm", value: "inactive", displayed: false)
	getAction("/armhome")
}

// This will only work if the appropriate output from the trigger module is connected to a zone wired as a keyswitch zone
def AlarmDisarm()
{
	// Send a disarm command to the alarm and log that it is changing
	 
	log.debug "disarm()"
	sendEvent(name: "armaway", value: "inactive", displayed: false)
	sendEvent(name: "armhome", value: "inactive", displayed: false)
	sendEvent(name: "disarm", value: "changing")
	getAction("/disarm")
}


def AlarmTrigger()
{
	log.debug "Triggering alarm"
	getAction("/triggeralarm?output=7")
}

def ZoneCreateZoneDevices()
{
	// This will create child devices
	// The child devices are created in the handler for the /getzonenames response
	log.debug "Requesting List of Alarm Zones"
	getAction("/getzonenames")
}

def ZoneRemoveZoneDevices()
{
	// This will remove all child devices
	log.debug "Removing Child Zone Devices"
	try
	{
		getChildDevices()?.each
		{
			try
			{
				deleteChildDevice(it.deviceNetworkId)
			}
			catch (e)
			{
				log.debug "Error deleting ${it.deviceNetworkId}, probably locked into a SmartApp: ${e}"
			}
		}
	}
	catch (err)
	{
		log.debug "Either no child devices exist or there was an error finding child devices: ${err}"
	}
}


def ZoneTestFunction()
{
	// This function creates a test device, which may be needed due to some random problems that exist when creating child devices
	// due to issues in the SmartThings API
	// log.debug "Got here inside the test function"
	log.debug prename
	if (device.currentValue("createzonedevices") == "cancreatezonedevices")
	{
		log.debug "state correct"
	}
	
	try
	{
		def curdevice = getChildDevices()?.find { it.deviceNetworkId == "alarmchildzonetest"}
		if (curdevice)
		{
			//Do nothing as we already have a test device
		}
		else
		{
			addChildDevice("smartthings", "Motion Detector", "alarmchildzonetest", device.hub.id, [name: "alarm zone test device", label: "Alarm Zone Test Device", completedSetup: true])
		}
	} 
	catch (e)
	{
		log.error "Couldnt find device, probably doesn't exist so safe to add a new one: ${e}"
		addChildDevice("smartthings", "Motion Detector", "alarmchildzonetest", device.hub.id, [name: "alarm zone test device", label: "Alarm Zone Test Device", completedSetup: true])
	}
}


// This will configure the device to talk to SmartThings
def configure()
{
	def cmds = []
	log.debug "Configuring Alarm (getting zones+types, configuring IP/port/timeout)"
	cmds << getAction("/status")
	
	def requeststring = "/config?ip_for_st=${device.hub.getDataValue("localIP")}&port_for_st=${device.hub.getDataValue("localSrvPortTCP")}"
	
	if (inactivityseconds?.isInteger())
	{
		// Inactivityseconds is both populated and an integer, so lets send it to the Wemos
		requeststring = requeststring + "&inactivity_seconds=${settings.inactivityseconds}"
	}
	
	// log.debug requeststring
	// Send the details to SmartThings
	cmds << getAction(requeststring)
	return cmds
}

def refresh()
{
	log.debug "refresh()"
	// SendEvents should be before any getAction, otherwise getAction does nothing
	sendEvent(name: "ip", value: device.hub.getDataValue("localIP")+"\r\nPort: "+device.hub.getDataValue("localSrvPortTCP"), displayed: false)
	// Now refresh Alarm status
	getAction("/refresh")
	// getAction("/getzonenames")
}

def installed()
{
	log.debug "installed()"
	//configure()
}

def updated()
{
	log.debug "updated()"
	configure()
}

def ping()
{
	log.debug "ping()"
	getAction("/ping")
}

// These functions are needed due to the alarm device capability and hence will serve to arm/disarm the alarm (though unlikely to be called)
def stop()
{
	// Disarming the alarm system via SmartThings is currently disabled
	// AlarmDisarm()
}

def off()
{
	// Disarming the alarm system via SmartThings is currently disabled
	// AlarmDisarm()
}

def strobe()
{
	AlarmArmHome()
}

def siren()
{
	AlarmArmAway()
}

def both()
{
	AlarmTrigger()
}

def parse(description)
{
	def map = [:]
	def events = []
	def cmds = []
	
	if(description == "updated") return
	def descMap = parseDescriptionAsMap(description)
	
	if (descMap == null)
	{
		log.debug "Not valid json response/message"
		log.debug description
		return
	}
	
	def body = new String(descMap["body"].decodeBase64())

	def slurper = new JsonSlurper()
	def result;
	try
	{
		result = slurper.parseText(body)
	}
	catch (e)
	{
		log.debug "Invalid response from system: " + body
		return
	}
	
	// Received an alarm stat string so update tile status but dont display it since the event string below will be logged
	if (result.containsKey("stat_str"))
	{
 		handleAlarmStatus(result.stat_str)
	}
	
	// If we receive a key containing 'stat_update_from' then it is an alarm status so add it to the event log and update tile
	if (result.containsKey("stat_update_from"))
	{
		def dateTime = new Date()
		def sensorStateChangedDate = dateTime.format("yyyy-MM-dd HH:mm", location.timeZone)
		def status_string = result.stat_str + " by " + result.stat_update_from + " at " + sensorStateChangedDate
		// Send the status string that we have built
		sendEvent(name: "events", value: "${status_string}", displayed: true, isStateChange: true)
	}
	
	if (result.containsKey("zone_status"))
	{
		handleZoneStatus(result.zone_id, result.zone_status)
	}
	
	// This code will pull zone information out of the /getzonenamespage (and allows for zones not in order)
	if (result.containsKey("maxzoneid"))
	{
		log.debug "Handling getzonenames page"
		handleCreateZones(result.zones)
	}
}


private handleAlarmStatus(statusString)
{
	switch (statusString)
	{
		case ["Disarmed", "Disarm", "Ready"]:
			sendEvent(name: "disarm", value: "active", displayed: false)
			sendEvent(name: "armaway", value: "inactive", displayed: false)
			sendEvent(name: "armhome", value: "inactive", displayed: false)
			sendEvent(name: "status", value: "disarmed", displayed: false)
			sendEvent(name: "alarm", value: "off", displayed: false)
			log.debug "Disarmed Status found"
			break
				
		case ["Not Ready"]:
			sendEvent(name: "disarm", value: "inactive", displayed: false)
			sendEvent(name: "armaway", value: "inactive", displayed: false)
			sendEvent(name: "armhome", value: "inactive", displayed: false)
			sendEvent(name: "status", value: "disarmed", displayed: false)
			sendEvent(name: "alarm", value: "off", displayed: false)
			log.debug "Not-ready Status found"
			break

		case ["Armed Away", "Arm Away"]:
			sendEvent(name: "disarm", value: "inactive", displayed: false)
			sendEvent(name: "armaway", value: "active", displayed: false)
			sendEvent(name: "armhome", value: "inactive", displayed: false)
			sendEvent(name: "status", value: "away", displayed: false)
			sendEvent(name: "alarm", value: "siren", displayed: false)
			log.debug "Armed Away Status found"
			break

		case ["Armed Home", "Arm Home"]:
			sendEvent(name: "disarm", value: "inactive", displayed: false)
			sendEvent(name: "armaway", value: "inactive", displayed: false)
			sendEvent(name: "armhome", value: "active", displayed: false)
			sendEvent(name: "status", value: "home", displayed: false)
			sendEvent(name: "alarm", value: "strobe", displayed: false)
			log.debug "Armed Home Status found"
			break

		case ["Exit Delay"]:
			sendEvent(name: "disarm", value: "inactive", displayed: false)
			sendEvent(name: "armaway", value: "changing")
			sendEvent(name: "armhome", value: "inactive", displayed: false)
			log.debug "Exit Delay Status found"
			break
 
		case ["Delay Alarm", "Confirm Alarm"]:
			sendEvent(name: "disarm", value: "inactive", displayed: false)
			sendEvent(name: "armaway", value: "inactive", displayed: false)
			sendEvent(name: "armhome", value: "inactive", displayed: false)
			sendEvent(name: "status", value: "alarm", displayed: false)
			sendEvent(name: "alarm", value: "both", displayed: false)
			log.debug "Alarm Status found - Uh Oh!!"
			break

		default:
			log.debug "Unknown Alarm state received = ${result.stat_str}"
			break
	}
}

private handleZoneStatus(zoneId, zoneStatus)
{
	def thisZoneDeviceId = "alarmchildzone"+zoneId
	def curdevice = null
	try
	{
		// Got a zone status so first try to find the correct child device
		curdevice = getChildDevices()?.find { it.deviceNetworkId == thisZoneDeviceId }
	}
	catch (e)
	{
		log.debug "Failed to find child zone for zone " + zoneId + "exception ${e}"
	}

	if (curdevice == null)
	{
		log.debug "Failed to find child device for zone: " + zoneId + " expecting " + thisZoneDeviceId
		/*
		def children = getChildDevices()

		log.debug "device has ${children.size()} children"
		children.each { child ->
		log.debug "child ${child.displayName} has deviceNetworkId ${child.deviceNetworkId}"
		}
		*/
	}
	else
	{
		// Check the device type for this child, since the different child device types need different event types
		boolean isMotionDevice = (curdevice.capabilities.find { it.name == "Motion Sensor" } != null)
		boolean isSmokeDevice = (curdevice.capabilities.find { it.name == "Smoke Detector" } != null)

		// Handle the specific zone status result
		switch (zoneStatus)
		{				  
			case "Active":
				log.debug "Got Active zone: " + zoneId + ", which is called - " + curdevice
				if (isMotionDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "active", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "motion", value: "active")
				}
				else if (isSmokeDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "smoke", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "smoke", value: "detected")					
				}
				else
				{
					sendEvent(name: "panelzone"+zoneId, value: "open", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "contact", value: "open")
				}
				break					

			case "Inactive":
				log.debug "Got Inactive zone: " + zoneId + ", which is called - " + curdevice
				if (isMotionDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "inactive", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "motion", value: "inactive")
				}
				else if (isSmokeDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "clear", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "smoke", value: "clear")					
				}
				else
				{
					sendEvent(name: "panelzone"+zoneId, value: "closed", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "contact", value: "closed")
				}
				break 			

			case "Bypassed - Active":
				log.debug "Got Active Bypassed zone: " + zoneId + ", which is called - " + curdevice
				sendEvent(name: "panelzone"+zoneId, value: "bypass", displayed: false, isStateChange: true)
				if (isMotionDevice)
				{
					curdevice?.sendEvent(name: "motion", value: "active")
				}
				else if (isSmokeDevice)
				{
					curdevice?.sendEvent(name: "smoke", value: "detected")					
				}
				else
				{
					curdevice?.sendEvent(name: "contact", value: "open")
				}
				break

			case "Bypassed - Inactive":
				log.debug "Got Inactive Bypassed zone: " + zoneId + ", which is called - " + curdevice
				sendEvent(name: "panelzone"+zoneId, value: "bypass", displayed: false, isStateChange: true)
				if (isMotionDevice)
				{
					curdevice?.sendEvent(name: "motion", value: "inactive")
				}
				else if (isSmokeDevice)
				{
					curdevice?.sendEvent(name: "smoke", value: "clear")					
				}
				else
				{
					curdevice?.sendEvent(name: "contact", value: "closed")
				}
				break

			case "Tamper":
				log.debug "Got Tamper for zone: " + zoneId + ", which is called - " + curdevice
				// We'll set it to open for now, since at least that gives an indication something is wrong!
				if (isMotionDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "active", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "motion", value: "active")
				}
				else if (isSmokeDevice)
				{
					sendEvent(name: "panelzone"+zoneId, value: "smoke", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "smoke", value: "detected")					
				}
				else
				{
					sendEvent(name: "panelzone"+zoneId, value: "open", displayed: false, isStateChange: true)
					curdevice?.sendEvent(name: "contact", value: "open")
				}
				break

			default:
				log.debug "Unknown status received: ${zoneId} is ${zoneStatus}"
				break
		}
	}
}

private handleCreateZones(zones)
{
	for (def curzone in zones)
	{
		// First update the name of the tile on this device
		sendEvent(name: "panelzonename"+(curzone.zoneid), value: curzone.zonename)

		// Now setup the prepend and postpend names for the new child zone device
		def thiszonename = ""
		if (prename != null) {thiszonename = thiszonename + prename + " "}
		thiszonename = thiszonename + curzone.zonename
		if (postname != null) {thiszonename = thiszonename + " " + postname}
				
		log.debug "Trying to add child with name: ${thiszonename}, ID: alarmchildzone${curzone.zoneid} to ${device.hub.id}"

		// Now try to find a child device with the right name - exception handler needed in case it fails to find any children
		try
		{
			def curchildzone = getChildDevices()?.find { it.deviceNetworkId == "alarmchildzone${curzone.zoneid}"}
		}
		catch (e)
		{
			// Would reach here if it cant find any children or that child doesnt exist so we can try and create it
			log.debug "Couldnt find device, probably doesn't exist so safe to add a new one: ${e}"
		}

		// If we don't have a matching child already, and the name isn't Unknown, then we can finally start creating the child device
		if ((curchildzone == null) && (curzone.zonename != "Unknown"))
		{
			try
			{
				switch (curzone.zonetype)
				{
					case ["Magnet", "Contact", "Entry/Exit"]:
						// If it is a magnetic sensor then add it as a contact sensor
						addChildDevice("smartthings", "Open/Closed Sensor", "alarmchildzone${curzone.zoneid}", device.hub.id, [name: thiszonename])
						log.debug "Created contact zone child device"
						break

					case ["Motion", "Interior", "Wired"]:
						// If it is a motion or interior sensor then add it as a motion detector device
						addChildDevice("smartthings", "Motion Detector", "alarmchildzone${curzone.zoneid}", device.hub.id, [name: thiszonename])
						log.debug "Created motion zone child device"
						break

					case ["Smoke", "Fire"]:
						try
						{
							addChildDevice("leefauto", "Smoke Detector", "alarmchildzone${curzone.zoneid}", device.hub.id, [name: thiszonename])
						}
						catch (e)
						{
							log.debug "Couldn't create Smoke Detector child device. Is device handler installed ? Creating motion detector zone instead."
							addChildDevice("smartthings", "Motion Detector", "alarmchildzone${curzone.zoneid}", device.hub.id, [name: thiszonename])
						}
						log.debug "Created Smoke Alarm zone child device"
						break
						
					case ["Shock", "Vibration", "Gas", "Panic", "KeySwitch"]:
						// Add the remainders as motion detectors for now - unfortunately this will display motion/no-motion instead of active/inactive 
						addChildDevice("smartthings", "Motion Detector", "alarmchildzone${curzone.zoneid}", device.hub.id, [name: thiszonename])
						log.debug "Create motion zone child device for non-motion zone"
						break

					default:
						log.debug "Unknown sensor found, we'll have to ignore for now"
						break
				}
			}
			catch (e)
			{
				log.error "Couldnt add device, probably already exists: ${e}"
			}
		}
	}
}

private getAction(uri)
{ 
	log.debug "uri ${uri}"
	updateDNI()

	def userpass

	if(password != null && password != "") 
		userpass = encodeCredentials("admin", password)
	
	def headers = getHeader(userpass)
  
	def hubAction = new physicalgraph.device.HubAction(
		method: "GET",
		path: uri,
		headers: headers
		)
	return hubAction	
}

def parseDescriptionAsMap(description)
{
	description.split(",").inject([:])
	{
		map, param ->
		def nameAndValue = param.split(":")
		if (nameAndValue.size() > 1)
			map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
	}
}

private getHeader(userpass = null)
{
	def headers = [:]
	headers.put("Host", getHostAddress())
	headers.put("Content-Type", "application/x-www-form-urlencoded")
	if (userpass != null)
	   headers.put("Authorization", userpass)
	return headers
}

private encodeCredentials(username, password)
{
	def userpassascii = "${username}:${password}"
	def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
	return userpass
}

private updateDNI()
{ 
	if (state.dni != null && state.dni != "" && device.deviceNetworkId != state.dni)
	{
	   device.deviceNetworkId = state.dni
	}
}

private getHostAddress()
{
	if(getDeviceDataByName("ip") && getDeviceDataByName("port"))
	{
		return "${getDeviceDataByName("ip")}:${getDeviceDataByName("port")}"
	}
	else
	{
		return "${ip}:80"
	}
}

This looks interesting.

For the most part, yes. There are some distinct changes needed for drivers, and for HTTP related methods. There are a handful of other system methods that are slightly different, and our button capability is very different.

What you can do is port the source code, and first, deal with any compile-time errors that show up before you can even save the code. Then, use Logs to discover errors the code might be throwing, and chase those down.

Could someone knowledgeable please answer some queries I have, that are related to the openness/closedness of apps/DH's.

On this thread over at ST, there was a debate going on as to whether HE is opensource or closed source.

My query is this: is it still possible to create and modify DH's and apps? If no, what are those users talking about? I'm not able to understand (yet) because I dont have a HE, and can not experience it.

Hi Mike,

Users are able to use the builtin drivers (DH's) and apps. The code for these, with a couple of exceptions, is not published and not able to be modified. That said, users are quite easily able to write their own apps and drivers and use those. A couple of notes; HE does have a basic z-wave parameter tool that lets users push parameter changes to their devices to suit their needs while continuing to use the built in drivers. The HE team is absolutely amazing at providing support for their platform. The team routinely adds support for new devices and, at least on one occasion, a user has loaned a device to the support team so that they could create a driver for it (Beat that ST!). The HE team and community routinely provide assistance to users developing their own drivers and apps. There are a number of very talented developers here writing some great apps for the community (and developing hardware as well!).

I read through some of those posts in the link you provided and will note that when one of the posters complaining about HE was here the support team did their best to help him. All I will say is that when someone has made up their mind that something isn't going to work for them, they will find a way to make that happen.

If your willing to try something new, HE is a turning out to be a great platform backed by a dedicated support team, that are not just employees but dedicated home automation enthusiast themselves and as such listen to, and understand, the needs of the community. I will also add that there are a lot of supportive, knowledgeable, members in the community here that have been able to help myself, and others, out whenever there have been issues.

5 Likes

Thanks, after being on the fence for a while, I put in an order yesterday.

2 Likes

Ditto...been on the fence...recent instability with ST brings me to start down another road....looking forward to it.

1 Like