From d18774081ac14c38dfca561f97c1670e2c5c5549 Mon Sep 17 00:00:00 2001 From: Eugen Valeriu Date: Tue, 14 Mar 2017 13:13:02 -0700 Subject: [PATCH] MSA-1839: SmartThings in any respect whatsoever. --- .../raspberry-pi.src/raspberry-pi.groovy | 250 ++++++++++++++++ ...-code-friday-virtual-device-manager.groovy | 277 ++++++++++++++++++ 2 files changed, 527 insertions(+) create mode 100644 devicetypes/nicholaswilde/smartthings/raspberry-pi.src/raspberry-pi.groovy create mode 100644 devicetypes/pstuart/live-code-friday-virtual-device-manager.src/live-code-friday-virtual-device-manager.groovy diff --git a/devicetypes/nicholaswilde/smartthings/raspberry-pi.src/raspberry-pi.groovy b/devicetypes/nicholaswilde/smartthings/raspberry-pi.src/raspberry-pi.groovy new file mode 100644 index 0000000..42edaa4 --- /dev/null +++ b/devicetypes/nicholaswilde/smartthings/raspberry-pi.src/raspberry-pi.groovy @@ -0,0 +1,250 @@ +/** + * Raspberry Pi + * + * Copyright 2014 Nicholas Wilde + * + * Monitor your Raspberry Pi using SmartThings and WebIOPi + * + * Companion WebIOPi python script can be found here: + * + * + * 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. + * + */ + +import groovy.json.JsonSlurper + +preferences { + input("ip", "string", title:"IP Address", description: "192.168.1.150", required: true, displayDuringSetup: true) + input("port", "string", title:"Port", description: "8000", defaultValue: 8000 , required: true, displayDuringSetup: true) + input("username", "string", title:"Username", description: "webiopi", required: true, displayDuringSetup: true) + input("password", "password", title:"Password", description: "Password", required: true, displayDuringSetup: true) +} + +metadata { + definition (name: "Raspberry Pi", namespace: "nicholaswilde/smartthings", author: "Nicholas Wilde") { + capability "Polling" + capability "Refresh" + capability "Temperature Measurement" + capability "Switch" + capability "Sensor" + capability "Actuator" + + attribute "cpuPercentage", "string" + attribute "memory", "string" + attribute "diskUsage", "string" + + command "restart" + } + + simulator { + // TODO: define status and reply messages here + } + + tiles { + valueTile("temperature", "device.temperature", width: 1, height: 1) { + state "temperature", label:'${currentValue}° CPU', unit: "F", + backgroundColors:[ + [value: 25, color: "#153591"], + [value: 35, color: "#1e9cbb"], + [value: 47, color: "#90d2a7"], + [value: 59, color: "#44b621"], + [value: 67, color: "#f1d801"], + [value: 76, color: "#d04e00"], + [value: 77, color: "#bc2323"] + ] + } + standardTile("button", "device.switch", width: 1, height: 1, canChangeIcon: true) { + state "off", label: 'Off', icon: "st.Electronics.electronics18", backgroundColor: "#ffffff", nextState: "on" + state "on", label: 'On', icon: "st.Electronics.electronics18", backgroundColor: "#79b821", nextState: "off" + } + valueTile("cpuPercentage", "device.cpuPercentage", inactiveLabel: false) { + state "default", label:'${currentValue}% CPU', unit:"Percentage", + backgroundColors:[ + [value: 31, color: "#153591"], + [value: 44, color: "#1e9cbb"], + [value: 59, color: "#90d2a7"], + [value: 74, color: "#44b621"], + [value: 84, color: "#f1d801"], + [value: 95, color: "#d04e00"], + [value: 96, color: "#bc2323"] + ] + } + valueTile("memory", "device.memory", width: 1, height: 1) { + state "default", label:'${currentValue} MB', unit:"MB", + backgroundColors:[ + [value: 353, color: "#153591"], + [value: 287, color: "#1e9cbb"], + [value: 210, color: "#90d2a7"], + [value: 133, color: "#44b621"], + [value: 82, color: "#f1d801"], + [value: 26, color: "#d04e00"], + [value: 20, color: "#bc2323"] + ] + } + valueTile("diskUsage", "device.diskUsage", width: 1, height: 1) { + state "default", label:'${currentValue}% Disk', unit:"Percent", + backgroundColors:[ + [value: 31, color: "#153591"], + [value: 44, color: "#1e9cbb"], + [value: 59, color: "#90d2a7"], + [value: 74, color: "#44b621"], + [value: 84, color: "#f1d801"], + [value: 95, color: "#d04e00"], + [value: 96, color: "#bc2323"] + ] + } + standardTile("restart", "device.restart", inactiveLabel: false, decoration: "flat") { + state "default", action:"restart", label: "Restart", displayName: "Restart" + } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") { + state "default", action:"refresh.refresh", icon: "st.secondary.refresh" + } + main "button" + details(["button", "temperature", "cpuPercentage", "memory" , "diskUsage", "restart", "refresh"]) + } +} + +// ------------------------------------------------------------------ + +// parse events into attributes +def parse(String description) { + def map = [:] + def descMap = parseDescriptionAsMap(description) + //log.debug descMap + def body = new String(descMap["body"].decodeBase64()) + //log.debug "body: ${body}" + def slurper = new JsonSlurper() + def result = slurper.parseText(body) + log.debug "result: ${result}" + if (result){ + log.debug "Computer is up" + sendEvent(name: "switch", value: "on") + } + + + if (result.containsKey("cpu_temp")) { + sendEvent(name: "temperature", value: result.cpu_temp) + } + + if (result.containsKey("cpu_perc")) { + sendEvent(name: "cpuPercentage", value: result.cpu_perc) + } + + if (result.containsKey("mem_avail")) { + log.debug "mem_avail: ${result.mem_avail}" + sendEvent(name: "memory", value: result.mem_avail) + } + if (result.containsKey("disk_usage")) { + log.debug "disk_usage: ${result.disk_usage}" + sendEvent(name: "diskUsage", value: result.disk_usage) + } + +} + +// handle commands +def poll() { + log.debug "Executing 'poll'" + sendEvent(name: "switch", value: "off") + getRPiData() +} + +def refresh() { + sendEvent(name: "switch", value: "off") + log.debug "Executing 'refresh'" + getRPiData() +} + +def restart(){ + log.debug "Restart was pressed" + sendEvent(name: "switch", value: "off") + def uri = "/macros/reboot" + postAction(uri) +} + +// Get CPU percentage reading +private getRPiData() { + def uri = "/macros/getData" + postAction(uri) +} + +// ------------------------------------------------------------------ + +private postAction(uri){ + setDeviceNetworkId(ip,port) + + def userpass = encodeCredentials(username, password) + + def headers = getHeader(userpass) + + def hubAction = new physicalgraph.device.HubAction( + method: "POST", + path: uri, + headers: headers + )//,delayAction(1000), refresh()] + log.debug("Executing hubAction on " + getHostAddress()) + //log.debug hubAction + hubAction +} + +// ------------------------------------------------------------------ +// Helper methods +// ------------------------------------------------------------------ + +def parseDescriptionAsMap(description) { + description.split(",").inject([:]) { map, param -> + def nameAndValue = param.split(":") + map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] + } +} + +private encodeCredentials(username, password){ + log.debug "Encoding credentials" + def userpassascii = "${username}:${password}" + def userpass = "Basic " + userpassascii.encodeAsBase64().toString() + //log.debug "ASCII credentials are ${userpassascii}" + //log.debug "Credentials are ${userpass}" + return userpass +} + +private getHeader(userpass){ + log.debug "Getting headers" + def headers = [:] + headers.put("HOST", getHostAddress()) + headers.put("Authorization", userpass) + //log.debug "Headers are ${headers}" + return headers +} + +private delayAction(long time) { + new physicalgraph.device.HubAction("delay $time") +} + +private setDeviceNetworkId(ip,port){ + def iphex = convertIPtoHex(ip) + def porthex = convertPortToHex(port) + device.deviceNetworkId = "$iphex:$porthex" + log.debug "Device Network Id set to ${iphex}:${porthex}" +} + +private getHostAddress() { + return "${ip}:${port}" +} + +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 +} \ No newline at end of file diff --git a/devicetypes/pstuart/live-code-friday-virtual-device-manager.src/live-code-friday-virtual-device-manager.groovy b/devicetypes/pstuart/live-code-friday-virtual-device-manager.src/live-code-friday-virtual-device-manager.groovy new file mode 100644 index 0000000..69d94ff --- /dev/null +++ b/devicetypes/pstuart/live-code-friday-virtual-device-manager.src/live-code-friday-virtual-device-manager.groovy @@ -0,0 +1,277 @@ +/** + * Live Code Friday Virtual Device Manager + * + * Copyright 2015 Patrick Stuart + * + * 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. + * + * Tasks + Create a dynamic pages interface + Pick a device type + add/delete that child + + */ +definition( + name: "Live Code Friday Virtual Device Manager", + namespace: "pstuart", + author: "Patrick Stuart", + description: "Live Code Friday Virtual Device Manager", + category: "My Apps", + iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", + iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", + iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", + singleInstance: true) + + +preferences { + page(name: "firstPage") + page(name: "inputPage") + page(name: "devicePage") + page(name: "addDevicePage") + page(name: "viewDevicePage") + page(name: "deletePage") +} + +def firstPage() { + dynamicPage(name: "firstPage", title: "Where to first?", install: true, uninstall: true) { + section("Main Menu") { + paragraph "Version 1.2" + href(page: "inputPage", title: "Let's Add Devices!") + + } + /* + section("Later") { + paragraph "More to come..." + } + */ + + } +} + +def inputPage() { + dynamicPage(name: "inputPage", title: "Choose What Device Type You Want To Add", nextPage:"firstPage") { + section("Device Types") { + href(page: "devicePage", title: "Switches", params: [device: "Generic Switch"]) + href(page: "devicePage", title: "Dimmers", params: [device: "Generic Dimmer"]) + href(page: "devicePage", title: "Contact Sensor", params: [device: "Generic Contact"]) + + } + section("Later") { + paragraph "more devices coming soon" + } + section("Navigation") { + href(page: "firstPage", title: "Main Menu") + } + } +} + +def devicePage(params) { + dynamicPage(name: "devicePage", title: "Devices", nextPage:"inputPage") { + // Loop childDevices based on type + // match up types + def device = params.device + log.debug "Hit Device Page with the selector device type $device" + def deviceTitle = device + "s" + if (device?.endsWith('ch') || device?.endsWith('s') || device?.endsWith('x')) { + deviceTitle = device + "es" + } + log.debug "Device Title is $deviceTitle" + + section("Installed ${deviceTitle}") { + def childDevices = getChildDevices() + + log.debug "The Child Devices are $childDevices" + if (childDevices) { + def devices = "Switches Installed:\r\nThis is a second line\r\n" + log.debug "Inside childDevices if statement" + + childDevices.findAll { it.typeName == device } + .each { + log.debug "The child device id is $it.deviceNetworkId and the type is $it" + def test = it.typeName + log.debug "Testing $test" + //def tempDevice = getChildDevice(it) + //log.debug tempDevice + //devices = devices + "test\r\n" + + + + switch(it.typeName) { + case "Generic Switch" : + href(page: "viewDevicePage", title: it.name, params: [dni: it.deviceNetworkId]) + break + case "Generic Dimmer" : + href(page: "viewDevicePage", title: it.name, params: [dni: it.deviceNetworkId]) + break + case "Generic Contact" : + href(page: "viewDevicePage", title: it.name, params: [dni: it.deviceNetworkId]) + break + default : break + + + } + + } + } else { + paragraph "No Virtual Generic Devices are Installed" + } + } + + section("Add A ${params.device}") { //${params.device} + // List Switches getChildDevices() + + // Add A Switch addChildDevice() + // View A Switch / Delete that switch go to switch view + input("DeviceName", "text") + href(page: "addDevicePage", title: "New $device", params: [type: device]) + } + + section("Navigation") { + href(page: "firstPage", title: "Main Menu") + } + } +} + +def addDevicePage(params) { + dynamicPage(name: "addDevicePage", title: "New $params.type", nextPage:"devicePage") { + section("New $params.type Add Result") { + //add new virtual switch + log.debug "Add Device Page hit with params $params and $settings.DeviceName" + def newDeviceName = params.type + if (settings.DeviceName) { + newDeviceName = settings.DeviceName + } + def result = addChildDevice(params.type, newDeviceName) + paragraph "$params.type Added ${result}" + + href(page: "devicePage", title: "Devices") + } + + section("Navigation") { + href(page: "firstPage", title: "Main Menu") + } + } +} + +def viewDevicePage(params) { + dynamicPage(name: "viewDevicePage", title: "Switch", nextPage:"devicePage") { + def viewSwitch = getChildDevice(params.dni) + section("$viewSwitch.name") { + paragraph "Switch Details \r\nName: $viewSwitch.name\r\nType: $viewSwitch.typeName\r\nNetwork ID: $viewSwitch.deviceNetworkId\r\nStates\r\nSwitch: ${viewSwitch.currentState('switch').value}\r\n" // Create info about switch / child device + log.debug viewSwitch.currentState('switch').value + href(page: "deletePage", title: "Delete", params: [dni: params.dni]) + } + + section("Navigation") { + href(page: "firstPage", title: "Main Menu") + } + } +} + +def deletePage(params) { + dynamicPage(name: "deletePage", title: "Delete", nextPage:"devicePage") { + section("switch") { + paragraph "Deleted Switch with DNI of $params.dni" + log.debug "Deleting $params.dni" + //def delete = getChildDevices().findAll { it?.contains(params.dni) } + //log.debug delete + def delete = getChildDevice(params.dni) + //removeChildDevices(delete) + deleteChildDevice(delete.deviceNetworkId) + + href(page: "switchPage", title: "Switches") + } + + section("Navigation") { + href(page: "firstPage", title: "Main Menu") + } + } +} + +def installed() { + log.debug "Installed with settings: ${settings}" + + initialize() +} + +def updated() { + log.debug "Updated with settings: ${settings}" + + unsubscribe() + initialize() +} + +def initialize() { + // TODO: subscribe to attributes, devices, locations, etc. + def switches = getChildDevices() + switches.each { + if (!it.currentValue('switch') ) { + it.off() + } + } +} + + +def addChildDevice(params, deviceName) { + //Get all devices installed as children + def childDevices = getChildDevices() //.findAll{ it -> it.type == params } //Find device of type params.type + //def collectDevices = childDevices.collect{ getChildDevice(it).type ?: 0} + //log.debug "The result of collectDevices is $collectDevices" + + def gTypes = genericTypes() + log.debug gTypes + def subChildDevices = childDevices?.findAll { it -> it.typeName == params } + log.debug "The subset of child devices is $subChildDevices based on type $params" + def counter = subChildDevices?.size() + 1 + log.debug "$subChildDevices and counter is $counter" + + /* + def counters = [:] + childDevices.each { + def childDevice = getChildDevice(it) + log.debug "Child Device type is $childDevice.type" + gTypes.each { + if (it == childDevice.type) { + def counter = ["name" : params.type, "counter" : counter++ ] + } + } + } */ + + + + + + //def counter = childDevices.size() + 1 //TODO Fix counter for each type + def dni = "pstuartDevice_$counter" // TODO create random string /guid + def newDeviceName = "$params $counter" + if (deviceName != params) { + newDeviceName = deviceName + } + log.debug newDeviceName + log.debug dni + log.debug params + log.trace "just about to add childe device" + def childDevice = addChildDevice("pstuart", params, dni, null, [name:newDeviceName]) + log.debug childDevice + childDevice.off() + return childDevice + //return dni + + +} + +def genericTypes() { + def gTypes = [ + "Generic Switch", + "Generic Contact", + "Generic Dimmer", + ] + return gTypes +} \ No newline at end of file