From 177c8163489d37a692d6b0a5dd2bc89644f3be6d Mon Sep 17 00:00:00 2001 From: obycode Date: Tue, 15 Dec 2015 10:46:27 -0600 Subject: [PATCH] MSA-747: BeaconThings is a simple app to let you integrate iBeacons into your SmartThings smart home. With BeaconThings, you can get more reliable, and more specific location information. Spread some beacons around your home, register them with BeaconThings, and it will tell SmartThings when you're nearby. For each BeaconThing your register, a device is added to SmartThings that can be used with SmartRules, or custom SmartApps, to trigger events. This submission contains the SmartApp to which the iOS app connects with OAuth, and the device type which is created by the SmartApp for each beacon registered in the app. --- .../beaconthing.src/beaconthing.groovy | 107 +++++++++++++ .../beaconthings-manager.groovy | 147 ++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 devicetypes/com-obycode/beaconthing.src/beaconthing.groovy create mode 100644 smartapps/com-obycode/beaconthings-manager.src/beaconthings-manager.groovy diff --git a/devicetypes/com-obycode/beaconthing.src/beaconthing.groovy b/devicetypes/com-obycode/beaconthing.src/beaconthing.groovy new file mode 100644 index 0000000..de1850c --- /dev/null +++ b/devicetypes/com-obycode/beaconthing.src/beaconthing.groovy @@ -0,0 +1,107 @@ +/** +* BeaconThing +* +* Copyright 2015 obycode +* +* 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 + +metadata { + definition (name: "BeaconThing", namespace: "com.obycode", author: "obycode") { + capability "Beacon" + capability "Presence Sensor" + capability "Sensor" + + attribute "inRange", "json_object" + attribute "inRangeFriendly", "string" + + command "setPresence", ["string"] + command "arrived", ["string"] + command "left", ["string"] + } + + simulator { + status "present": "presence: 1" + status "not present": "presence: 0" + } + + tiles { + standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { + state("present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0") + state("not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff") + } + valueTile("inRange", "device.inRangeFriendly", inactiveLabel: true, height:1, width:3, decoration: "flat") { + state "default", label:'${currentValue}', backgroundColor:"#ffffff" + } + main "presence" + details (["presence","inRange"]) + } +} + +// parse events into attributes +def parse(String description) { + log.debug "Parsing '${description}'" +} + +def installed() { + sendEvent(name: "presence", value: "not present") + def emptyList = [] + def json = new groovy.json.JsonBuilder(emptyList) + sendEvent(name:"inRange", value:json.toString()) +} + +def setPresence(status) { + log.debug "Status is $status" + sendEvent(name:"presence", value:status) +} + +def arrived(id) { + log.debug "$id has arrived" + def theList = device.latestValue("inRange") + def inRangeList = new JsonSlurper().parseText(theList) + if (inRangeList.contains(id)) { + return + } + inRangeList += id + def json = new groovy.json.JsonBuilder(inRangeList) + log.debug "Now in range: ${json.toString()}" + sendEvent(name:"inRange", value:json.toString()) + + // Generate human friendly string for tile + def friendlyList = "Nearby: " + inRangeList.join(", ") + sendEvent(name:"inRangeFriendly", value:friendlyList) + + if (inRangeList.size() == 1) { + setPresence("present") + } +} + +def left(id) { + log.debug "$id has left" + def theList = device.latestValue("inRange") + def inRangeList = new JsonSlurper().parseText(theList) + inRangeList -= id + def json = new groovy.json.JsonBuilder(inRangeList) + log.debug "Now in range: ${json.toString()}" + sendEvent(name:"inRange", value:json.toString()) + + // Generate human friendly string for tile + def friendlyList = "Nearby: " + inRangeList.join(", ") + + if (inRangeList.empty) { + setPresence("not present") + friendlyList = "No one is nearby" + } + + sendEvent(name:"inRangeFriendly", value:friendlyList) +} diff --git a/smartapps/com-obycode/beaconthings-manager.src/beaconthings-manager.groovy b/smartapps/com-obycode/beaconthings-manager.src/beaconthings-manager.groovy new file mode 100644 index 0000000..3254f32 --- /dev/null +++ b/smartapps/com-obycode/beaconthings-manager.src/beaconthings-manager.groovy @@ -0,0 +1,147 @@ +/** + * BeaconThing Manager + * + * Copyright 2015 obycode + * + * 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. + * + */ +definition( + name: "BeaconThings Manager", + namespace: "com.obycode", + author: "obycode", + description: "SmartApp to interact with the BeaconThings iOS app. Use this app to integrate iBeacons into your smart home.", + category: "Convenience", + iconUrl: "http://beaconthingsapp.com/images/Icon-60.png", + iconX2Url: "http://beaconthingsapp.com/images/Icon-60@2x.png", + iconX3Url: "http://beaconthingsapp.com/images/Icon-60@3x.png", + oauth: true) + + +preferences { + section("Allow BeaconThings to talk to your home") { + + } +} + +def installed() { + log.debug "Installed with settings: ${settings}" + + initialize() +} + +def initialize() { +} + +def uninstalled() { + removeChildDevices(getChildDevices()) +} + +mappings { + path("/beacons") { + action: [ + DELETE: "clearBeacons", + POST: "addBeacon" + ] + } + + path("/beacons/:id") { + action: [ + PUT: "updateBeacon", + DELETE: "deleteBeacon" + ] + } +} + +void clearBeacons() { + removeChildDevices(getChildDevices()) +} + +void addBeacon() { + def beacon = request.JSON?.beacon + if (beacon) { + def beaconId = "BeaconThings" + if (beacon.major) { + beaconId = "$beaconId-${beacon.major}" + if (beacon.minor) { + beaconId = "$beaconId-${beacon.minor}" + } + } + log.debug "adding beacon $beaconId" + def d = addChildDevice("com.obycode", "BeaconThing", beaconId, null, [label:beacon.name, name:"BeaconThing", completedSetup: true]) + log.debug "addChildDevice returned $d" + + if (beacon.present) { + d.arrive(beacon.present) + } + else if (beacon.presence) { + d.setPresence(beacon.presence) + } + } +} + +void updateBeacon() { + log.debug "updating beacon ${params.id}" + def beaconDevice = getChildDevice(params.id) + // def children = getChildDevices() + // def beaconDevice = children.find{ d -> d.deviceNetworkId == "${params.id}" } + if (!beaconDevice) { + log.debug "Beacon not found directly" + def children = getChildDevices() + beaconDevice = children.find{ d -> d.deviceNetworkId == "${params.id}" } + if (!beaconDevice) { + log.debug "Beacon not found in list either" + return + } + } + + // This could be just updating the presence + def presence = request.JSON?.presence + if (presence) { + log.debug "Setting ${beaconDevice.label} to $presence" + beaconDevice.setPresence(presence) + } + + // It could be someone arriving + def arrived = request.JSON?.arrived + if (arrived) { + log.debug "$arrived arrived at ${beaconDevice.label}" + beaconDevice.arrived(arrived) + } + + // It could be someone left + def left = request.JSON?.left + if (left) { + log.debug "$left left ${beaconDevice.label}" + beaconDevice.left(left) + } + + // or it could be updating the name + def beacon = request.JSON?.beacon + if (beacon) { + beaconDevice.label = beacon.name + } +} + +void deleteBeacon() { + log.debug "deleting beacon ${params.id}" + deleteChildDevice(params.id) + // def children = getChildDevices() + // def beaconDevice = children.find{ d -> d.deviceNetworkId == "${params.id}" } + // if (beaconDevice) { + // deleteChildDevice(beaconDevice.deviceNetworkId) + // } +} + +private removeChildDevices(delete) { + delete.each { + deleteChildDevice(it.deviceNetworkId) + } +}