mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 05:11:52 +00:00
Compare commits
2 Commits
MSA-1886-1
...
MSA-685-7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
068979faad | ||
|
|
01f13ed4a5 |
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* obything Music Player
|
||||
*
|
||||
* Copyright 2015 obycode
|
||||
*/
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
metadata {
|
||||
definition (name: "obything Music Player", namespace: "com.obycode", author: "obycode") {
|
||||
capability "Music Player"
|
||||
capability "Refresh"
|
||||
capability "Switch"
|
||||
|
||||
// These strings are comma separated lists of names
|
||||
attribute "playlists", "json_object"
|
||||
attribute "speakers", "json_object"
|
||||
attribute "playlistDescription", "string"
|
||||
|
||||
// playPlaylist(String uri, speakers=null, volume=null, resume=false, restore=false)
|
||||
command "playPlaylist", ["string", "string", "number", "number", "number"]
|
||||
// playTrack(String uri, speakers=null, volume=null, resume=false, restore=false, playlist=null)
|
||||
command "playTrack", ["string", "string", "number", "number", "number", "string"]
|
||||
|
||||
command "update", ["string"]
|
||||
}
|
||||
|
||||
simulator {
|
||||
// TODO: define status and reply messages here
|
||||
}
|
||||
|
||||
tiles {
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"richmusic", type:"lighting", width:6, height:4) {
|
||||
tileAttribute("device.status", key: "PRIMARY_CONTROL") {
|
||||
attributeState "paused", label: 'Paused', action:"music Player.play", icon:"http://obything.obycode.com/icons/obything-device.png", backgroundColor:"#D0D0D0"
|
||||
attributeState "stopped", label: 'Stopped', action:"music Player.play", icon:"http://obything.obycode.com/icons/obything-device.png", backgroundColor:"#D0D0D0"
|
||||
attributeState "playing", label:'Playing', action:"music Player.pause", icon:"http://obything.obycode.com/icons/obything-device.png", backgroundColor:"#4C4CFF"
|
||||
}
|
||||
tileAttribute("device.trackDescription", key: "SECONDARY_CONTROL") {
|
||||
attributeState "default", label:'${currentValue}'
|
||||
}
|
||||
tileAttribute("device.level", key: "SLIDER_CONTROL") {
|
||||
attributeState "level", action:"music Player.setLevel", range:"(0..100)"
|
||||
}
|
||||
}
|
||||
|
||||
standardTile("nextTrack", "device.status", width: 2, height: 2, decoration: "flat") {
|
||||
state "next", label:'', action:"music Player.nextTrack", icon:"st.sonos.next-btn", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("playpause", "device.status", width: 2, height: 2, decoration: "flat") {
|
||||
state "default", label:'', action:"music Player.play", icon:"st.sonos.play-btn", backgroundColor:"#ffffff"
|
||||
state "playing", label:'', action:"music Player.pause", icon:"st.sonos.pause-btn", backgroundColor:"#ffffff"
|
||||
state "paused", label:'', action:"music Player.play", icon:"st.sonos.play-btn", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("previousTrack", "device.status", width: 2, height: 2, decoration: "flat") {
|
||||
state "previous", label:'', action:"music Player.previousTrack", icon:"st.sonos.previous-btn", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("refresh", "device.status", width: 2, height: 2, decoration: "flat") {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh", backgroundColor:"#ffffff"
|
||||
}
|
||||
standardTile("mute", "device.mute", width: 2, height: 2, decoration: "flat") {
|
||||
state "unmuted", label:"Mute", action:"music Player.mute", icon:"st.custom.sonos.unmuted", backgroundColor:"#ffffff"
|
||||
state "muted", label:"Unmute", action:"music Player.unmute", icon:"st.custom.sonos.muted", backgroundColor:"#ffffff"
|
||||
}
|
||||
controlTile("levelSliderControl", "device.level", "slider", height: 2, width: 4) {
|
||||
state "level", label:"Volume", action:"music Player.setLevel", backgroundColor:"#ffffff"
|
||||
}
|
||||
valueTile("currentPlaylist", "device.playlistDescription", height:2, width:6, decoration: "flat") {
|
||||
state "default", label:'${currentValue}', backgroundColor:"#ffffff"
|
||||
}
|
||||
|
||||
main "richmusic"
|
||||
details(["richmusic",
|
||||
"previousTrack","nextTrack","refresh",
|
||||
"levelSliderControl","mute",
|
||||
"currentPlaylist"
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse events into attributes
|
||||
def parse(String description) {
|
||||
// log.debug "parse called with $description"
|
||||
def map = stringToMap(description)
|
||||
if (map.headers && map.body) { //got device info response
|
||||
if (map.body) {
|
||||
def bodyString = new String(map.body.decodeBase64())
|
||||
def slurper = new JsonSlurper()
|
||||
def result = slurper.parseText(bodyString)
|
||||
parseMessage(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def parseMessage(message) {
|
||||
log.debug "message is $message"
|
||||
if (message.containsKey("volume")) {
|
||||
log.debug "setting volume to ${message.volume}"
|
||||
sendEvent(name: "level", value: message.volume)
|
||||
}
|
||||
if (message.containsKey("mute")) {
|
||||
log.debug "setting mute to ${message.mute}"
|
||||
sendEvent(name: "mute", value: message.mute)
|
||||
}
|
||||
if (message.containsKey("status")) {
|
||||
log.debug "setting status to ${message.status}"
|
||||
sendEvent(name: "status", value: message.status)
|
||||
}
|
||||
if (message.containsKey("trackData")) {
|
||||
def json = new groovy.json.JsonBuilder(message.trackData)
|
||||
log.debug "setting trackData to ${json.toString()}"
|
||||
sendEvent(name: "trackData", value: json.toString())
|
||||
}
|
||||
if (message.containsKey("trackDescription")) {
|
||||
log.debug "setting trackDescription info to ${message.trackDescription}"
|
||||
sendEvent(name: "trackDescription", value: message.trackDescription)
|
||||
}
|
||||
if (message.containsKey("playlistData")) {
|
||||
def json = new groovy.json.JsonBuilder(message.playlistData)
|
||||
log.debug "setting playlistData to ${json.toString()}"
|
||||
sendEvent(name: "playlistData", value: json.toString())
|
||||
}
|
||||
if (message.containsKey("playlistDescription")) {
|
||||
log.debug "setting playlistDescription info to ${message.playlistDescription}"
|
||||
sendEvent(name: "playlistDescription", value: message.playlistDescription)
|
||||
}
|
||||
if (message.containsKey("playlists")) {
|
||||
def json = new groovy.json.JsonBuilder(message.playlists)
|
||||
log.debug "setting playlists to ${json.toString()}"
|
||||
sendEvent(name: "playlists",value: json.toString())
|
||||
}
|
||||
if (message.containsKey("speakers")) {
|
||||
def json = new groovy.json.JsonBuilder(message.speakers)
|
||||
log.debug "setting speakers to ${json.toString()}"
|
||||
sendEvent(name: "speakers",value: json.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// Called by service manager to send updates from device
|
||||
def update(message) {
|
||||
log.debug "update: $message"
|
||||
parseMessage(message)
|
||||
}
|
||||
|
||||
def installed() {
|
||||
// Refresh to get current state
|
||||
refresh()
|
||||
}
|
||||
|
||||
// handle commands
|
||||
def refresh() {
|
||||
log.debug "Executing 'refresh'"
|
||||
getInfo("command=refresh")
|
||||
}
|
||||
|
||||
def on() {
|
||||
log.debug "Executing 'on' (play)"
|
||||
sendCommand("command=play")
|
||||
}
|
||||
|
||||
def off() {
|
||||
log.debug "Executing 'off' (pause)"
|
||||
sendCommand("command=pause")
|
||||
}
|
||||
|
||||
def play() {
|
||||
log.debug "Executing 'play'"
|
||||
sendCommand("command=play")
|
||||
}
|
||||
|
||||
def pause() {
|
||||
log.debug "Executing 'pause'"
|
||||
sendCommand("command=pause")
|
||||
}
|
||||
|
||||
def stop() {
|
||||
log.debug "Executing 'stop'"
|
||||
sendCommand("command=stop")
|
||||
}
|
||||
|
||||
def nextTrack() {
|
||||
log.debug "Executing 'nextTrack'"
|
||||
sendCommand("command=next")
|
||||
}
|
||||
|
||||
def setLevel(value) {
|
||||
log.debug "Executing 'setLevel' to $value"
|
||||
sendCommand("command=volume&level=$value")
|
||||
}
|
||||
|
||||
// def playText(String msg) {
|
||||
// log.debug "Executing 'playText'"
|
||||
// sendCommand("say=$msg")
|
||||
// }
|
||||
//
|
||||
def mute() {
|
||||
log.debug "Executing 'mute'"
|
||||
sendCommand("command=mute")
|
||||
}
|
||||
|
||||
def previousTrack() {
|
||||
log.debug "Executing 'previousTrack'"
|
||||
sendCommand("command=previous")
|
||||
}
|
||||
|
||||
def unmute() {
|
||||
log.debug "Executing 'unmute'"
|
||||
sendCommand("command=unmute")
|
||||
}
|
||||
|
||||
def playPlaylist(String uri, speakers=null, volume=null) {
|
||||
log.trace "playPlaylist($uri, $speakers, $volume, $resume, $restore)"
|
||||
def command = "command=playlist&name=${uri}"
|
||||
if (speakers) {
|
||||
command += "&speakers=${speakers}"
|
||||
}
|
||||
if (volume) {
|
||||
command += "&volume=${volume}"
|
||||
}
|
||||
sendCommand(command)
|
||||
}
|
||||
|
||||
def playTrack(String uri, speakers=null, volume=null, resume=false, restore=false, playlist=null) {
|
||||
log.trace "playTrack($uri, $speakers, $volume, $resume, $restore, $playlist)"
|
||||
def command = "command=track&url=${uri}"
|
||||
if (speakers) {
|
||||
command += "&speakers=${speakers}"
|
||||
}
|
||||
if (volume) {
|
||||
command += "&volume=${volume}"
|
||||
}
|
||||
if (resume) {
|
||||
command += "&resume="
|
||||
}
|
||||
else if (restore) {
|
||||
command += "&restore="
|
||||
}
|
||||
if (playlist) {
|
||||
command += "&playlist=$playlist"
|
||||
}
|
||||
sendCommand(command)
|
||||
}
|
||||
|
||||
// def speak(text) {
|
||||
// def url = textToSpeech(text)
|
||||
// sendCommand("playTrack&track=${url.uri}&resume")
|
||||
// }
|
||||
//
|
||||
// def beep() {
|
||||
// sendCommand("beep")
|
||||
// }
|
||||
|
||||
// Private functions used internally
|
||||
private Integer convertHexToInt(hex) {
|
||||
Integer.parseInt(hex,16)
|
||||
}
|
||||
|
||||
|
||||
private String convertHexToIP(hex) {
|
||||
[convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
|
||||
}
|
||||
|
||||
private getHostAddress() {
|
||||
def parts = device.deviceNetworkId.split(":")
|
||||
def ip = convertHexToIP(parts[0])
|
||||
def port = convertHexToInt(parts[1])
|
||||
return ip + ":" + port
|
||||
}
|
||||
|
||||
private sendCommand(command) {
|
||||
def path = "/player&" + command
|
||||
|
||||
def result = new physicalgraph.device.HubAction(
|
||||
method: "POST",
|
||||
path: path,
|
||||
headers: [
|
||||
HOST: getHostAddress()
|
||||
],
|
||||
)
|
||||
result
|
||||
}
|
||||
|
||||
private getInfo(command) {
|
||||
def path = "/player&" + command
|
||||
|
||||
def result = new physicalgraph.device.HubAction(
|
||||
method: "GET",
|
||||
path: path,
|
||||
headers: [
|
||||
HOST: getHostAddress()
|
||||
],
|
||||
)
|
||||
result
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* ObyThing Music Connect
|
||||
*
|
||||
* Copyright 2015 obycode
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "obything Connect",
|
||||
namespace: "com.obycode",
|
||||
author: "obycode",
|
||||
description: "Smart home, smart Mac. With obything.",
|
||||
category: "Fun & Social",
|
||||
iconUrl: "http://obything.obycode.com/icons/icon60.png",
|
||||
iconX2Url: "http://obything.obycode.com/icons/icon120.png",
|
||||
iconX3Url: "http://obything.obycode.com/icons/icon120.png",
|
||||
oauth: [displayName: "obything", displayLink: "http://obything.obycode.com"])
|
||||
|
||||
|
||||
preferences {
|
||||
section("Smart home, smart Mac. With obything.") {
|
||||
app(name: "childApps", appName: "obything Notify with Sound", namespace: "com.obycode", title: "Notify with Sound", multiple: true)
|
||||
app(name: "childApps", appName: "obything Music Control", namespace: "com.obycode", title: "Music Control", multiple: true)
|
||||
app(name: "childApps", appName: "obything Trigger Playlist", namespace: "com.obycode", title: "Trigger Playlists", multiple: true)
|
||||
app(name: "childApps", appName: "obything Weather Forecast", namespace: "com.obycode", title: "Weather Forecast", multiple: true)
|
||||
}
|
||||
}
|
||||
|
||||
mappings {
|
||||
path("/setup") {
|
||||
action: [
|
||||
POST: "setup",
|
||||
]
|
||||
}
|
||||
path("/:uuid/:kind") {
|
||||
action: [
|
||||
POST: "createChild",
|
||||
PUT: "updateChild",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed"
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated"
|
||||
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
def uninstalled() {
|
||||
logout()
|
||||
}
|
||||
|
||||
def logout() {
|
||||
revokeAccessToken()
|
||||
}
|
||||
|
||||
// mapping handlers
|
||||
|
||||
// /setup POST
|
||||
def setup() {
|
||||
def ip = request.JSON?.ip
|
||||
if (ip == null) {
|
||||
return httpError(400, "IP not specified")
|
||||
}
|
||||
def port = request.JSON?.port
|
||||
if (port == null) {
|
||||
return httpError(400, "Port not specified")
|
||||
}
|
||||
def name = request.JSON?.name
|
||||
if (name == null) {
|
||||
return httpError(400, "Name not specified")
|
||||
}
|
||||
def uuid = request.JSON?.uuid
|
||||
if (uuid == null) {
|
||||
return httpError(400, "UUID not specified")
|
||||
}
|
||||
|
||||
// If machines is not initialized yet, set it up
|
||||
if (state.machines == null) {
|
||||
state.machines = [:]
|
||||
}
|
||||
|
||||
// If this machine has already been initialized, just update it
|
||||
if (state.machines[uuid]) {
|
||||
state.machines[uuid]["ip"] = ip
|
||||
state.machines[uuid]["port"] = port
|
||||
state.machines[uuid]["name"] = name
|
||||
log.debug "Updated machine"
|
||||
|
||||
def dead = []
|
||||
// Update the child devices
|
||||
def newHexIP = convertIPtoHex(ip)
|
||||
state.machines[uuid]["children"].keySet().each {
|
||||
def ids = state.machines[uuid]["children"][it]
|
||||
def child = getChildDevice(ids.dni)
|
||||
if (!child) {
|
||||
dead.add(it)
|
||||
}
|
||||
else {
|
||||
// Only change the IP; the label could've been manually changed and I'm
|
||||
// not sure how to handle the port changing (its not allowed now anyway).
|
||||
def oldHexPort = child.deviceNetworkId.split(':')[1]
|
||||
def newDNI = "$newHexIP:$oldHexPort"
|
||||
child.deviceNetworkId = newDNI
|
||||
state.machines[uuid]["children"][it]["dni"] = newDNI
|
||||
}
|
||||
}
|
||||
dead.each {
|
||||
state.machines[uuid]["children"].remove(it)
|
||||
}
|
||||
}
|
||||
// Otherwise, just create a new machine
|
||||
else {
|
||||
def machine = [ip:ip, port:port, name:name, children:[:]]
|
||||
state.machines[uuid] = machine
|
||||
log.debug "Added new machine"
|
||||
}
|
||||
|
||||
sendCommand(state.machines[uuid], "/ping")
|
||||
}
|
||||
|
||||
private removeChildDevices(delete) {
|
||||
delete.each {
|
||||
deleteChildDevice(it.deviceNetworkId)
|
||||
}
|
||||
}
|
||||
|
||||
// /:uuid/:kind POST
|
||||
def createChild() {
|
||||
// Constants for the port offsets
|
||||
final int iTunesService = 1
|
||||
final int pandoraService = 2
|
||||
final int spotifyService = 3
|
||||
|
||||
def machine = state.machines[params.uuid]
|
||||
if (machine == null) {
|
||||
return httpError(404, "Machine not found")
|
||||
}
|
||||
|
||||
def childName = machine["name"]
|
||||
def portNum = machine["port"].toInteger()
|
||||
switch (params.kind) {
|
||||
case "itunes":
|
||||
childName = childName + " iTunes"
|
||||
portNum += iTunesService
|
||||
break
|
||||
case "pandora":
|
||||
childName = childName + " Pandora"
|
||||
portNum += pandoraService
|
||||
break
|
||||
case "spotify":
|
||||
childName = childName + " Spotify"
|
||||
portNum += spotifyService
|
||||
break
|
||||
default:
|
||||
return httpError(400, "Unknown or unspecified device type")
|
||||
}
|
||||
|
||||
def hexIP = convertIPtoHex(machine["ip"])
|
||||
def hexPort = convertPortToHex(portNum.toString())
|
||||
def childId = "$hexIP:$hexPort"
|
||||
|
||||
// If this child already exists, re-associate with it
|
||||
def existing = getChildDevice(childId)
|
||||
if (existing) {
|
||||
log.debug "Found existing device: $existing"
|
||||
state.machines[params.uuid]["children"][params.kind] = [id:existing.id, dni:childId]
|
||||
}
|
||||
// otherwise, create it
|
||||
else {
|
||||
def d = addChildDevice("com.obycode", "obything Music Player", childId, location.hubs[0].id, [name:"obything Music Player", label:childName, completedSetup:true])
|
||||
log.debug "Created child device: $d"
|
||||
state.machines[params.uuid]["children"][params.kind] = [id:d.id, dni:d.deviceNetworkId]
|
||||
}
|
||||
|
||||
return [dni:childId]
|
||||
}
|
||||
|
||||
// /:uuid/:kind PUT
|
||||
def updateChild() {
|
||||
def machine = state.machines[params.uuid]
|
||||
if (machine == null) {
|
||||
return httpError(404, "Machine not found")
|
||||
}
|
||||
|
||||
def child = machine["children"][params.kind]
|
||||
if (child == null) {
|
||||
return httpError(404, "Device not found")
|
||||
}
|
||||
|
||||
def device = getChildDevice(child.dni)
|
||||
if (device == null) {
|
||||
return httpError(404, "Device not found")
|
||||
}
|
||||
|
||||
device.update(request.JSON)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
private void sendCommand(machine, path, command = null) {
|
||||
def fullPath = path
|
||||
if (command) {
|
||||
fullPath = fullPath + "?" + command
|
||||
}
|
||||
sendHubCommand(new physicalgraph.device.HubAction("GET " + fullPath + """ HTTP/1.1\r\nHOST: """ + machine["ip"] + ":" + machine["port"] + """\r\n\r\n""", physicalgraph.device.Protocol.LAN))
|
||||
}
|
||||
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Music Control
|
||||
*
|
||||
* Author: obything
|
||||
*
|
||||
* Date: 2015-11-09
|
||||
*/
|
||||
definition(
|
||||
name: "obything Music Control",
|
||||
namespace: "com.obycode",
|
||||
author: "obycode, based on Music Control by SmartThings",
|
||||
description: "Play or pause your music when certain actions take place in your home.",
|
||||
category: "Convenience",
|
||||
iconUrl: "http://obything.obycode.com/icons/obything-device.png",
|
||||
iconX2Url: "http://obything.obycode.com/icons/obything-device.png"
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Control your music when something happens", install: true, uninstall: true)
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
input "starting", "time", title: "Starting", required: false
|
||||
input "ending", "time", title: "Ending", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def mainPage() {
|
||||
dynamicPage(name: "mainPage") {
|
||||
def anythingSet = anythingSet()
|
||||
if (anythingSet) {
|
||||
section("When..."){
|
||||
ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
|
||||
ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
|
||||
ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
}
|
||||
section(anythingSet ? "Select additional triggers" : "When...", hideable: anythingSet, hidden: true){
|
||||
ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
|
||||
ifUnset "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
|
||||
ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
section("Perform this action"){
|
||||
input "actionType", "enum", title: "Action?", required: true, defaultValue: "play", options: [
|
||||
"Play",
|
||||
"Stop Playing",
|
||||
"Toggle Play/Pause",
|
||||
"Skip to Next Track",
|
||||
"Play Previous Track"
|
||||
]
|
||||
}
|
||||
section {
|
||||
input "obything", "capability.musicPlayer", title: "obything Music player", required: true
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "volume", "number", title: "Set the volume volume", description: "0-100%", required: false
|
||||
input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
|
||||
href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
|
||||
input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
|
||||
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
||||
if (settings.modes) {
|
||||
input "modes", "mode", title: "Only when mode is", multiple: true, required: false
|
||||
}
|
||||
input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
|
||||
}
|
||||
section([mobileOnly:true]) {
|
||||
label title: "Assign a name", required: false
|
||||
mode title: "Set for specific mode(s)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private anythingSet() {
|
||||
for (name in ["motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","triggerModes","timeOfDay"]) {
|
||||
if (settings[name]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private ifUnset(Map options, String name, String capability) {
|
||||
if (!settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
private ifSet(Map options, String name, String capability) {
|
||||
if (settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
unschedule()
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def subscribeToEvents() {
|
||||
log.trace "subscribeToEvents()"
|
||||
subscribe(app, appTouchHandler)
|
||||
subscribe(contact, "contact.open", eventHandler)
|
||||
subscribe(contactClosed, "contact.closed", eventHandler)
|
||||
subscribe(acceleration, "acceleration.active", eventHandler)
|
||||
subscribe(motion, "motion.active", eventHandler)
|
||||
subscribe(mySwitch, "switch.on", eventHandler)
|
||||
subscribe(mySwitchOff, "switch.off", eventHandler)
|
||||
subscribe(arrivalPresence, "presence.present", eventHandler)
|
||||
subscribe(departurePresence, "presence.not present", eventHandler)
|
||||
subscribe(smoke, "smoke.detected", eventHandler)
|
||||
subscribe(smoke, "smoke.tested", eventHandler)
|
||||
subscribe(smoke, "carbonMonoxide.detected", eventHandler)
|
||||
subscribe(water, "water.wet", eventHandler)
|
||||
subscribe(button1, "button.pushed", eventHandler)
|
||||
|
||||
if (triggerModes) {
|
||||
subscribe(location, modeChangeHandler)
|
||||
}
|
||||
|
||||
if (timeOfDay) {
|
||||
schedule(timeOfDay, scheduledTimeHandler)
|
||||
}
|
||||
}
|
||||
|
||||
def eventHandler(evt) {
|
||||
if (allOk) {
|
||||
def lastTime = state[frequencyKey(evt)]
|
||||
if (oncePerDayOk(lastTime)) {
|
||||
if (frequency) {
|
||||
if (lastTime == null || now() - lastTime >= frequency * 60000) {
|
||||
takeAction(evt)
|
||||
}
|
||||
else {
|
||||
log.debug "Not taking action because $frequency minutes have not elapsed since last action"
|
||||
}
|
||||
}
|
||||
else {
|
||||
takeAction(evt)
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.debug "Not taking action because it was already taken today"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def modeChangeHandler(evt) {
|
||||
log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
|
||||
if (evt.value in triggerModes) {
|
||||
eventHandler(evt)
|
||||
}
|
||||
}
|
||||
|
||||
def scheduledTimeHandler() {
|
||||
eventHandler(null)
|
||||
}
|
||||
|
||||
def appTouchHandler(evt) {
|
||||
takeAction(evt)
|
||||
}
|
||||
|
||||
private takeAction(evt) {
|
||||
log.debug "takeAction($actionType)"
|
||||
def options = [:]
|
||||
if (volume) {
|
||||
obything.setLevel(volume as Integer)
|
||||
options.delay = 1000
|
||||
}
|
||||
|
||||
switch (actionType) {
|
||||
case "Play":
|
||||
obything.play()
|
||||
break
|
||||
case "Stop Playing":
|
||||
obything.pause()
|
||||
break
|
||||
case "Toggle Play/Pause":
|
||||
def currentStatus = obything.currentValue("status")
|
||||
log.debug "Current status is $currentStatus"
|
||||
if (currentStatus == "playing") {
|
||||
obything.pause()
|
||||
}
|
||||
else {
|
||||
obything.play()
|
||||
}
|
||||
break
|
||||
case "Skip to Next Track":
|
||||
obything.nextTrack()
|
||||
break
|
||||
case "Play Previous Track":
|
||||
obything.previousTrack()
|
||||
break
|
||||
default:
|
||||
log.error "Action type '$actionType' not defined"
|
||||
}
|
||||
|
||||
if (frequency) {
|
||||
state.lastActionTimeStamp = now()
|
||||
}
|
||||
}
|
||||
|
||||
private frequencyKey(evt) {
|
||||
//evt.deviceId ?: evt.value
|
||||
"lastActionTimeStamp"
|
||||
}
|
||||
|
||||
private dayString(Date date) {
|
||||
def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
df.format(date)
|
||||
}
|
||||
|
||||
private oncePerDayOk(Long lastTime) {
|
||||
def result = true
|
||||
if (oncePerDay) {
|
||||
result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
|
||||
log.trace "oncePerDayOk = $result"
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// TODO - centralize somehow
|
||||
private getAllOk() {
|
||||
modeOk && daysOk && timeOk
|
||||
}
|
||||
|
||||
private getModeOk() {
|
||||
def result = !modes || modes.contains(location.mode)
|
||||
log.trace "modeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getDaysOk() {
|
||||
def result = true
|
||||
if (days) {
|
||||
def df = new java.text.SimpleDateFormat("EEEE")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
def day = df.format(new Date())
|
||||
result = days.contains(day)
|
||||
}
|
||||
log.trace "daysOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getTimeOk() {
|
||||
def result = true
|
||||
if (starting && ending) {
|
||||
def currTime = now()
|
||||
def start = timeToday(starting).time
|
||||
def stop = timeToday(ending).time
|
||||
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
|
||||
}
|
||||
log.trace "timeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private hhmm(time, fmt = "h:mm a")
|
||||
{
|
||||
def t = timeToday(time, location.timeZone)
|
||||
def f = new java.text.SimpleDateFormat(fmt)
|
||||
f.setTimeZone(location.timeZone ?: timeZone(time))
|
||||
f.format(t)
|
||||
}
|
||||
|
||||
private timeIntervalLabel()
|
||||
{
|
||||
(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
|
||||
}
|
||||
// TODO - End Centralize
|
||||
@@ -0,0 +1,410 @@
|
||||
/**
|
||||
* obything Notify with Sound
|
||||
*
|
||||
* Author: obycode
|
||||
* Date: 2015-08-30
|
||||
*/
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
definition(
|
||||
name: "obything Notify with Sound",
|
||||
namespace: "com.obycode",
|
||||
author: "obycode, based on 'Sonos Notify with Sound' by SmartThings",
|
||||
description: "Play a sound or custom message through your Mac with obything when the mode changes or other events occur.",
|
||||
category: "Convenience",
|
||||
iconUrl: "http://obything.obycode.com/icons/obything-device.png",
|
||||
iconX2Url: "http://obything.obycode.com/icons/obything-device.png",
|
||||
parent: "com.obycode:obything Connect"
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Play a message over your speakers when something happens", install: true, uninstall: true)
|
||||
page(name: "chooseTrack", title: "Select a playlist")
|
||||
page(name: "chooseSpeakers", title: "Select speakers")
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
input "starting", "time", title: "Starting", required: false
|
||||
input "ending", "time", title: "Ending", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def mainPage() {
|
||||
dynamicPage(name: "mainPage") {
|
||||
def anythingSet = anythingSet()
|
||||
if (anythingSet) {
|
||||
section("Play message when"){
|
||||
ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
|
||||
ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
|
||||
ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
}
|
||||
def hideable = anythingSet || app.installationState == "COMPLETE"
|
||||
def sectionTitle = anythingSet ? "Select additional triggers" : "Play message when..."
|
||||
|
||||
section(sectionTitle, hideable: hideable, hidden: true){
|
||||
ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
|
||||
ifUnset "triggerModes", "mode", title: "System Changes Mode", description: "Select mode(s)", required: false, multiple: true
|
||||
ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
section{
|
||||
input "actionType", "enum", title: "Action?", required: true, defaultValue: "Custom Message", options: [
|
||||
"Custom Message",
|
||||
"Custom URL",
|
||||
"Bell 1",
|
||||
"Bell 2",
|
||||
"Dogs Barking",
|
||||
"Fire Alarm",
|
||||
"The mail has arrived",
|
||||
"A door opened",
|
||||
"There is motion",
|
||||
"Smartthings detected a flood",
|
||||
"Smartthings detected smoke",
|
||||
"Someone is arriving",
|
||||
"Piano",
|
||||
"Lightsaber"]
|
||||
input "message","text",title:"Play this message", required:false, multiple: false
|
||||
input "url","text",title:"Play a sound at this URL", required:false, multiple: false
|
||||
}
|
||||
section {
|
||||
input "obything", "capability.musicPlayer", title: "On this obything iTunes device", required: true
|
||||
}
|
||||
section {
|
||||
href "chooseSpeakers", title: "With these speakers", description: speakers ? speakers : "Tap to set", state: speakers ? "complete" : "incomplete"
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "resumePlaying", "bool", title: "Resume currently playing music after notification", required: false, defaultValue: true
|
||||
href "chooseTrack", title: "Or play this music or radio station", description: playlist ? playlist : "Tap to set", state: playlist ? "complete" : "incomplete"
|
||||
|
||||
input "volume", "number", title: "Temporarily change volume", description: "0-100%", required: false
|
||||
input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
|
||||
href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
|
||||
input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
|
||||
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
||||
if (settings.modes) {
|
||||
input "modes", "mode", title: "Only when mode is", multiple: true, required: false
|
||||
}
|
||||
input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
|
||||
}
|
||||
section([mobileOnly:true]) {
|
||||
label title: "Assign a name", required: false
|
||||
mode title: "Set for specific mode(s)", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def chooseTrack() {
|
||||
dynamicPage(name: "chooseTrack") {
|
||||
section{
|
||||
input "playlist", "enum", title:"Play this playlist", required:true, multiple: false, options: playlistOptions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private playlistOptions() {
|
||||
def playlistString = obything.currentValue("playlists")
|
||||
log.debug "Playlists are $playlistString"
|
||||
def jsonList = new JsonSlurper().parseText(playlistString)
|
||||
jsonList.collect {
|
||||
it.Name
|
||||
}
|
||||
}
|
||||
|
||||
def chooseSpeakers() {
|
||||
dynamicPage(name: "chooseSpeakers") {
|
||||
section{
|
||||
input "speakers", "enum", title:"Play on these speakers", required:false, multiple: true, options: speakerOptions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private speakerOptions() {
|
||||
def speakersString = obything.currentValue("speakers")
|
||||
log.debug "Speakers are $speakersString"
|
||||
def slurper = new JsonSlurper()
|
||||
slurper.parseText(speakersString)
|
||||
}
|
||||
|
||||
private anythingSet() {
|
||||
for (name in ["motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","timeOfDay","triggerModes","timeOfDay"]) {
|
||||
if (settings[name]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private ifUnset(Map options, String name, String capability) {
|
||||
if (!settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
private ifSet(Map options, String name, String capability) {
|
||||
if (settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
unschedule()
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def subscribeToEvents() {
|
||||
subscribe(app, appTouchHandler)
|
||||
subscribe(contact, "contact.open", eventHandler)
|
||||
subscribe(contactClosed, "contact.closed", eventHandler)
|
||||
subscribe(acceleration, "acceleration.active", eventHandler)
|
||||
subscribe(motion, "motion.active", eventHandler)
|
||||
subscribe(mySwitch, "switch.on", eventHandler)
|
||||
subscribe(mySwitchOff, "switch.off", eventHandler)
|
||||
subscribe(arrivalPresence, "presence.present", eventHandler)
|
||||
subscribe(departurePresence, "presence.not present", eventHandler)
|
||||
subscribe(smoke, "smoke.detected", eventHandler)
|
||||
subscribe(smoke, "smoke.tested", eventHandler)
|
||||
subscribe(smoke, "carbonMonoxide.detected", eventHandler)
|
||||
subscribe(water, "water.wet", eventHandler)
|
||||
subscribe(button1, "button.pushed", eventHandler)
|
||||
|
||||
if (triggerModes) {
|
||||
subscribe(location, modeChangeHandler)
|
||||
}
|
||||
|
||||
if (timeOfDay) {
|
||||
runDaily(timeOfDay, scheduledTimeHandler)
|
||||
}
|
||||
|
||||
loadText()
|
||||
}
|
||||
|
||||
def eventHandler(evt) {
|
||||
log.trace "eventHandler($evt?.name: $evt?.value)"
|
||||
if (allOk) {
|
||||
log.trace "allOk"
|
||||
def lastTime = state[frequencyKey(evt)]
|
||||
if (oncePerDayOk(lastTime)) {
|
||||
if (frequency) {
|
||||
if (lastTime == null || now() - lastTime >= frequency * 60000) {
|
||||
takeAction(evt)
|
||||
}
|
||||
else {
|
||||
log.debug "Not taking action because $frequency minutes have not elapsed since last action"
|
||||
}
|
||||
}
|
||||
else {
|
||||
takeAction(evt)
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.debug "Not taking action because it was already taken today"
|
||||
}
|
||||
}
|
||||
}
|
||||
def modeChangeHandler(evt) {
|
||||
log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
|
||||
if (evt.value in triggerModes) {
|
||||
eventHandler(evt)
|
||||
}
|
||||
}
|
||||
|
||||
def scheduledTimeHandler() {
|
||||
eventHandler(null)
|
||||
}
|
||||
|
||||
def appTouchHandler(evt) {
|
||||
takeAction(evt)
|
||||
}
|
||||
|
||||
private takeAction(evt) {
|
||||
log.trace "takeAction()"
|
||||
|
||||
def speakerString
|
||||
if (speakers) {
|
||||
speakerString = ""
|
||||
speakers.each {
|
||||
speakerString += "\"$it\","
|
||||
}
|
||||
// remove the last comma and encode
|
||||
speakerString = encode(speakerString[0..-2])
|
||||
}
|
||||
|
||||
if (playlist) {
|
||||
obything.playTrack(state.sound.uri, speakerString, volume, resumePlaying, playlist)
|
||||
}
|
||||
else {
|
||||
obything.playTrack(state.sound.uri, speakerString, volume, resumePlaying)
|
||||
}
|
||||
|
||||
if (frequency || oncePerDay) {
|
||||
state[frequencyKey(evt)] = now()
|
||||
}
|
||||
log.trace "Exiting takeAction()"
|
||||
}
|
||||
|
||||
private frequencyKey(evt) {
|
||||
"lastActionTimeStamp"
|
||||
}
|
||||
|
||||
private dayString(Date date) {
|
||||
def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
df.format(date)
|
||||
}
|
||||
|
||||
private oncePerDayOk(Long lastTime) {
|
||||
def result = true
|
||||
if (oncePerDay) {
|
||||
result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
|
||||
log.trace "oncePerDayOk = $result"
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// TODO - centralize somehow
|
||||
private getAllOk() {
|
||||
modeOk && daysOk && timeOk
|
||||
}
|
||||
|
||||
private getModeOk() {
|
||||
def result = !modes || modes.contains(location.mode)
|
||||
log.trace "modeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getDaysOk() {
|
||||
def result = true
|
||||
if (days) {
|
||||
def df = new java.text.SimpleDateFormat("EEEE")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
def day = df.format(new Date())
|
||||
result = days.contains(day)
|
||||
}
|
||||
log.trace "daysOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getTimeOk() {
|
||||
def result = true
|
||||
if (starting && ending) {
|
||||
def currTime = now()
|
||||
def start = timeToday(starting).time
|
||||
def stop = timeToday(ending).time
|
||||
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
|
||||
}
|
||||
log.trace "timeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private hhmm(time, fmt = "h:mm a")
|
||||
{
|
||||
def t = timeToday(time, location.timeZone)
|
||||
def f = new java.text.SimpleDateFormat(fmt)
|
||||
f.setTimeZone(location.timeZone ?: timeZone(time))
|
||||
f.format(t)
|
||||
}
|
||||
|
||||
private getTimeLabel()
|
||||
{
|
||||
(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
|
||||
}
|
||||
// TODO - End Centralize
|
||||
|
||||
private loadText() {
|
||||
switch ( actionType) {
|
||||
case "Bell 1":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell1.mp3", duration: "10"]
|
||||
break;
|
||||
case "Bell 2":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell2.mp3", duration: "10"]
|
||||
break;
|
||||
case "Dogs Barking":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/dogs.mp3", duration: "10"]
|
||||
break;
|
||||
case "Fire Alarm":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/alarm.mp3", duration: "17"]
|
||||
break;
|
||||
case "The mail has arrived":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/the+mail+has+arrived.mp3", duration: "1"]
|
||||
break;
|
||||
case "A door opened":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/a+door+opened.mp3", duration: "1"]
|
||||
break;
|
||||
case "There is motion":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/there+is+motion.mp3", duration: "1"]
|
||||
break;
|
||||
case "Smartthings detected a flood":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/smartthings+detected+a+flood.mp3", duration: "2"]
|
||||
break;
|
||||
case "Smartthings detected smoke":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/smartthings+detected+smoke.mp3", duration: "1"]
|
||||
break;
|
||||
case "Someone is arriving":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/someone+is+arriving.mp3", duration: "1"]
|
||||
break;
|
||||
case "Piano":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/piano2.mp3", duration: "10"]
|
||||
break;
|
||||
case "Lightsaber":
|
||||
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/lightsaber.mp3", duration: "10"]
|
||||
break;
|
||||
case "Custom Message":
|
||||
if (message) {
|
||||
log.debug "message is $message"
|
||||
state.sound = textToSpeech(message) // instanceof List ? message[0] : message) // not sure why this is (sometimes) needed)
|
||||
}
|
||||
else {
|
||||
state.sound = textToSpeech("You selected the custom message option but did not enter a message in the $app.label Smart App")
|
||||
}
|
||||
break;
|
||||
case "Custom URL":
|
||||
if (url) {
|
||||
state.sound = [uri: url, duration: "0"]
|
||||
}
|
||||
else {
|
||||
state.sound = textToSpeech("You selected the custom URL option but did not enter a URL in the $app.label Smart App")
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log.debug "Invalid selection."
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* obything Trigger Playlist
|
||||
*
|
||||
* Author: obycode
|
||||
* Date: 2015-11-05
|
||||
*/
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
definition(
|
||||
name: "obything Trigger Playlist",
|
||||
namespace: "com.obycode",
|
||||
author: "obycode, based on Sonos Mood Music by SmartThings",
|
||||
description: "Plays a selected playlist on your Mac with obything",
|
||||
category: "Convenience",
|
||||
iconUrl: "http://obything.obycode.com/icons/obything-device.png",
|
||||
iconX2Url: "http://obything.obycode.com/icons/obything-device.png"
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Choose the trigger(s)", nextPage: "chooseTrackAndSpeakers", uninstall: true)
|
||||
page(name: "chooseTrackAndSpeakers", title: "Choose the playlist and speaker(s)", install: true, uninstall: true)
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
input "starting", "time", title: "Starting", required: false
|
||||
input "ending", "time", title: "Ending", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def mainPage() {
|
||||
dynamicPage(name: "mainPage") {
|
||||
def anythingSet = anythingSet()
|
||||
if (anythingSet) {
|
||||
section("Play music when..."){
|
||||
ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true
|
||||
ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
|
||||
ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
}
|
||||
|
||||
def hideable = anythingSet || app.installationState == "COMPLETE"
|
||||
def sectionTitle = anythingSet ? "Select additional triggers" : "Play music when..."
|
||||
|
||||
section(sectionTitle, hideable: hideable, hidden: true){
|
||||
ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true
|
||||
ifUnset "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
|
||||
ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
section {
|
||||
input "obything", "capability.musicPlayer", title: "On this obything music player", required: true
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "volume", "number", title: "Set the volume", description: "0-100%", required: false
|
||||
input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
|
||||
href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
|
||||
input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
|
||||
options: ["Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
if (settings.modes) {
|
||||
input "modes", "mode", title: "Only when mode is", multiple: true, required: false
|
||||
}
|
||||
input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def chooseTrackAndSpeakers() {
|
||||
dynamicPage(name: "chooseTrackAndSpeakers") {
|
||||
section {
|
||||
input "playlist", "enum", title:"Play this playlist", required:true, multiple: false, options: playlistOptions()
|
||||
input "speakers", "enum", title:"On these speakers", required:false, multiple: true, options: speakerOptions()
|
||||
}
|
||||
section([mobileOnly:true]) {
|
||||
label title: "Assign a name", required: false
|
||||
mode title: "Set for specific mode(s)", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private playlistOptions() {
|
||||
def playlistString = obything.currentValue("playlists")
|
||||
log.debug "Playlists are $playlistString"
|
||||
def jsonList = new JsonSlurper().parseText(playlistString)
|
||||
log.debug("jsonList is $jsonList")
|
||||
jsonList.collect {
|
||||
it.name
|
||||
}
|
||||
}
|
||||
|
||||
def chooseSpeakers() {
|
||||
dynamicPage(name: "chooseSpeakers") {
|
||||
section{
|
||||
input "speakers", "enum", title:"Play on these speakers", required:false, multiple: true, options: speakerOptions()
|
||||
}
|
||||
section([mobileOnly:true]) {
|
||||
label title: "Assign a name", required: false
|
||||
mode title: "Set for specific mode(s)", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private speakerOptions() {
|
||||
def speakersString = obything.currentValue("speakers")
|
||||
log.debug "Speakers are $speakersString"
|
||||
def slurper = new JsonSlurper()
|
||||
slurper.parseText(speakersString)
|
||||
}
|
||||
|
||||
private anythingSet() {
|
||||
for (name in ["motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","timeOfDay","triggerModes","timeOfDay"]) {
|
||||
if (settings[name]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private ifUnset(Map options, String name, String capability) {
|
||||
if (!settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
private ifSet(Map options, String name, String capability) {
|
||||
if (settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
unschedule()
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def subscribeToEvents() {
|
||||
log.trace "subscribeToEvents()"
|
||||
|
||||
subscribe(app, appTouchHandler)
|
||||
subscribe(contact, "contact.open", eventHandler)
|
||||
subscribe(contactClosed, "contact.closed", eventHandler)
|
||||
subscribe(acceleration, "acceleration.active", eventHandler)
|
||||
subscribe(motion, "motion.active", eventHandler)
|
||||
subscribe(mySwitch, "switch.on", eventHandler)
|
||||
subscribe(mySwitchOff, "switch.off", eventHandler)
|
||||
subscribe(arrivalPresence, "presence.present", eventHandler)
|
||||
subscribe(departurePresence, "presence.not present", eventHandler)
|
||||
subscribe(smoke, "smoke.detected", eventHandler)
|
||||
subscribe(smoke, "smoke.tested", eventHandler)
|
||||
subscribe(smoke, "carbonMonoxide.detected", eventHandler)
|
||||
subscribe(water, "water.wet", eventHandler)
|
||||
subscribe(button1, "button.pushed", eventHandler)
|
||||
|
||||
if (triggerModes) {
|
||||
subscribe(location, modeChangeHandler)
|
||||
}
|
||||
|
||||
if (timeOfDay) {
|
||||
runDaily(timeOfDay, scheduledTimeHandler)
|
||||
}
|
||||
}
|
||||
|
||||
def eventHandler(evt) {
|
||||
log.debug "In eventHandler"
|
||||
if (allOk) {
|
||||
if (frequency) {
|
||||
def lastTime = state[frequencyKey(evt)]
|
||||
if (lastTime == null || now() - lastTime >= frequency * 60000) {
|
||||
takeAction(evt)
|
||||
}
|
||||
}
|
||||
else {
|
||||
takeAction(evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def modeChangeHandler(evt) {
|
||||
log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
|
||||
if (evt.value in triggerModes) {
|
||||
eventHandler(evt)
|
||||
}
|
||||
}
|
||||
|
||||
def scheduledTimeHandler() {
|
||||
eventHandler(null)
|
||||
}
|
||||
|
||||
def appTouchHandler(evt) {
|
||||
takeAction(evt)
|
||||
}
|
||||
|
||||
private takeAction(evt) {
|
||||
log.info "Playing '$playlist'"
|
||||
def speakerString
|
||||
if (speakers) {
|
||||
speakerString = ""
|
||||
speakers.each {
|
||||
speakerString += "\"$it\","
|
||||
}
|
||||
// remove the last comma and encode
|
||||
speakerString = encode(speakerString[0..-2])
|
||||
}
|
||||
obything.playPlaylist(encode(playlist), speakerString, volume)
|
||||
|
||||
if (frequency || oncePerDay) {
|
||||
state[frequencyKey(evt)] = now()
|
||||
}
|
||||
}
|
||||
|
||||
private frequencyKey(evt) {
|
||||
"lastActionTimeStamp"
|
||||
}
|
||||
|
||||
private encode(text) {
|
||||
return URLEncoder.encode(text).replaceAll("\\+", "%20")
|
||||
}
|
||||
|
||||
private dayString(Date date) {
|
||||
def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
df.format(date)
|
||||
}
|
||||
|
||||
private oncePerDayOk(Long lastTime) {
|
||||
def result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
|
||||
log.trace "oncePerDayOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
// TODO - centralize somehow
|
||||
private getAllOk() {
|
||||
modeOk && daysOk && timeOk
|
||||
}
|
||||
|
||||
private getModeOk() {
|
||||
def result = !modes || modes.contains(location.mode)
|
||||
log.trace "modeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getDaysOk() {
|
||||
def result = true
|
||||
if (days) {
|
||||
def df = new java.text.SimpleDateFormat("EEEE")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
def day = df.format(new Date())
|
||||
result = days.contains(day)
|
||||
}
|
||||
log.trace "daysOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getTimeOk() {
|
||||
def result = true
|
||||
if (starting && ending) {
|
||||
def currTime = now()
|
||||
def start = timeToday(starting).time
|
||||
def stop = timeToday(ending).time
|
||||
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
|
||||
}
|
||||
log.trace "timeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private hhmm(time, fmt = "h:mm a")
|
||||
{
|
||||
def t = timeToday(time, location.timeZone)
|
||||
def f = new java.text.SimpleDateFormat(fmt)
|
||||
f.setTimeZone(location.timeZone ?: timeZone(time))
|
||||
f.format(t)
|
||||
}
|
||||
|
||||
private timeIntervalLabel()
|
||||
{
|
||||
(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* obything Weather Forecast
|
||||
*
|
||||
* Author: obycode
|
||||
* Date: 2014-10-13
|
||||
*/
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
definition(
|
||||
name: "obything Weather Forecast",
|
||||
namespace: "com.obycode",
|
||||
author: "obycode, based on Sonos Weather Forecast by SmartThings",
|
||||
description: "Play a weather report through your Mac or AirPlay speakers when the mode changes or other events occur",
|
||||
category: "Convenience",
|
||||
iconUrl: "http://obything.obycode.com/icons/obything-device.png",
|
||||
iconX2Url: "http://obything.obycode.com/icons/obything-device.png"
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "mainPage", title: "Play the weather report on your AirPlay speakers", install: true, uninstall: true)
|
||||
page(name: "chooseTrack", title: "Select a playlist")
|
||||
page(name: "chooseSpeakers", title: "Select speakers")
|
||||
page(name: "timeIntervalInput", title: "Only during a certain time") {
|
||||
section {
|
||||
input "starting", "time", title: "Starting", required: false
|
||||
input "ending", "time", title: "Ending", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def mainPage() {
|
||||
dynamicPage(name: "mainPage") {
|
||||
def anythingSet = anythingSet()
|
||||
if (anythingSet) {
|
||||
section("Play weather report when"){
|
||||
ifSet "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifSet "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifSet "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifSet "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifSet "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifSet "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifSet "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifSet "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifSet "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifSet "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifSet "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
|
||||
ifSet "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
|
||||
ifSet "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
}
|
||||
def hideable = anythingSet || app.installationState == "COMPLETE"
|
||||
def sectionTitle = anythingSet ? "Select additional triggers" : "Play weather report when..."
|
||||
|
||||
section(sectionTitle, hideable: hideable, hidden: true){
|
||||
ifUnset "motion", "capability.motionSensor", title: "Motion Here", required: false, multiple: true
|
||||
ifUnset "contact", "capability.contactSensor", title: "Contact Opens", required: false, multiple: true
|
||||
ifUnset "contactClosed", "capability.contactSensor", title: "Contact Closes", required: false, multiple: true
|
||||
ifUnset "acceleration", "capability.accelerationSensor", title: "Acceleration Detected", required: false, multiple: true
|
||||
ifUnset "mySwitch", "capability.switch", title: "Switch Turned On", required: false, multiple: true
|
||||
ifUnset "mySwitchOff", "capability.switch", title: "Switch Turned Off", required: false, multiple: true
|
||||
ifUnset "arrivalPresence", "capability.presenceSensor", title: "Arrival Of", required: false, multiple: true
|
||||
ifUnset "departurePresence", "capability.presenceSensor", title: "Departure Of", required: false, multiple: true
|
||||
ifUnset "smoke", "capability.smokeDetector", title: "Smoke Detected", required: false, multiple: true
|
||||
ifUnset "water", "capability.waterSensor", title: "Water Sensor Wet", required: false, multiple: true
|
||||
ifUnset "button1", "capability.button", title: "Button Press", required:false, multiple:true //remove from production
|
||||
ifUnset "triggerModes", "mode", title: "System Changes Mode", required: false, multiple: true
|
||||
ifUnset "timeOfDay", "time", title: "At a Scheduled Time", required: false
|
||||
}
|
||||
section {
|
||||
input("forecastOptions", "enum", defaultValue: "0", title: "Weather report options", description: "Select one or more", multiple: true,
|
||||
options: [
|
||||
["0": "Current Conditions"],
|
||||
["1": "Today's Forecast"],
|
||||
["2": "Tonight's Forecast"],
|
||||
["3": "Tomorrow's Forecast"],
|
||||
]
|
||||
)
|
||||
}
|
||||
section {
|
||||
input "obything", "capability.musicPlayer", title: "On this obything iTunes device", required: true
|
||||
}
|
||||
section {
|
||||
href "chooseSpeakers", title: "With these speakers", description: speakers ? speakers : "Tap to set", state: speakers ? "complete" : "incomplete"
|
||||
}
|
||||
section("More options", hideable: true, hidden: true) {
|
||||
input "resumePlaying", "bool", title: "Resume currently playing music after weather report finishes", required: false, defaultValue: true
|
||||
href "choosePlaylist", title: "Or play this playlist", description: playlist ? playlist : "Tap to set", state: playlist ? "complete" : "incomplete"
|
||||
|
||||
input "zipCode", "text", title: "Zip Code", required: false
|
||||
input "volume", "number", title: "Temporarily change volume", description: "0-100%", required: false
|
||||
input "frequency", "decimal", title: "Minimum time between actions (defaults to every event)", description: "Minutes", required: false
|
||||
href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : "incomplete"
|
||||
input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
|
||||
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
||||
if (settings.modes) {
|
||||
input "modes", "mode", title: "Only when mode is", multiple: true, required: false
|
||||
}
|
||||
input "oncePerDay", "bool", title: "Only once per day", required: false, defaultValue: false
|
||||
}
|
||||
section([mobileOnly:true]) {
|
||||
label title: "Assign a name", required: false
|
||||
mode title: "Set for specific mode(s)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def chooseTrack() {
|
||||
dynamicPage(name: "chooseTrack") {
|
||||
section{
|
||||
input "playlist", "enum", title:"Play this playlist", required:true, multiple: false, options: playlistOptions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def chooseSpeakers() {
|
||||
dynamicPage(name: "chooseSpeakers") {
|
||||
section{
|
||||
input "speakers", "enum", title:"Play on these speakers", required:false, multiple: true, options: speakerOptions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private speakerOptions() {
|
||||
def speakersString = obything.currentValue("speakers")
|
||||
log.debug "Speakers are $speakersString"
|
||||
def slurper = new JsonSlurper()
|
||||
slurper.parseText(speakersString)
|
||||
}
|
||||
|
||||
private anythingSet() {
|
||||
for (name in ["motion","contact","contactClosed","acceleration","mySwitch","mySwitchOff","arrivalPresence","departurePresence","smoke","water","button1","timeOfDay","triggerModes"]) {
|
||||
if (settings[name]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private ifUnset(Map options, String name, String capability) {
|
||||
if (!settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
private ifSet(Map options, String name, String capability) {
|
||||
if (settings[name]) {
|
||||
input(options, name, capability)
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
unschedule()
|
||||
subscribeToEvents()
|
||||
}
|
||||
|
||||
def subscribeToEvents() {
|
||||
subscribe(app, appTouchHandler)
|
||||
subscribe(contact, "contact.open", eventHandler)
|
||||
subscribe(contactClosed, "contact.closed", eventHandler)
|
||||
subscribe(acceleration, "acceleration.active", eventHandler)
|
||||
subscribe(motion, "motion.active", eventHandler)
|
||||
subscribe(mySwitch, "switch.on", eventHandler)
|
||||
subscribe(mySwitchOff, "switch.off", eventHandler)
|
||||
subscribe(arrivalPresence, "presence.present", eventHandler)
|
||||
subscribe(departurePresence, "presence.not present", eventHandler)
|
||||
subscribe(smoke, "smoke.detected", eventHandler)
|
||||
subscribe(smoke, "smoke.tested", eventHandler)
|
||||
subscribe(smoke, "carbonMonoxide.detected", eventHandler)
|
||||
subscribe(water, "water.wet", eventHandler)
|
||||
subscribe(button1, "button.pushed", eventHandler)
|
||||
|
||||
if (triggerModes) {
|
||||
subscribe(location,modeChangeHandler)
|
||||
}
|
||||
|
||||
if (timeOfDay) {
|
||||
runDaily(timeOfDay, scheduledTimeHandler)
|
||||
}
|
||||
}
|
||||
|
||||
def eventHandler(evt) {
|
||||
log.trace "eventHandler($evt?.name: $evt?.value)"
|
||||
if (allOk) {
|
||||
log.trace "allOk"
|
||||
def lastTime = state[frequencyKey(evt)]
|
||||
if (oncePerDayOk(lastTime)) {
|
||||
if (frequency) {
|
||||
if (lastTime == null || now() - lastTime >= frequency * 60000) {
|
||||
takeAction(evt)
|
||||
}
|
||||
else {
|
||||
log.debug "Not taking action because $frequency minutes have not elapsed since last action"
|
||||
}
|
||||
}
|
||||
else {
|
||||
takeAction(evt)
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.debug "Not taking action because it was already taken today"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def modeChangeHandler(evt) {
|
||||
log.trace "modeChangeHandler $evt.name: $evt.value ($triggerModes)"
|
||||
if (evt.value in triggerModes) {
|
||||
eventHandler(evt)
|
||||
}
|
||||
}
|
||||
|
||||
def scheduledTimeHandler() {
|
||||
eventHandler(null)
|
||||
}
|
||||
|
||||
def appTouchHandler(evt) {
|
||||
takeAction(evt)
|
||||
}
|
||||
|
||||
private takeAction(evt) {
|
||||
|
||||
loadText()
|
||||
|
||||
def speakerString
|
||||
if (speakers) {
|
||||
speakerString = ""
|
||||
speakers.each {
|
||||
speakerString += "$it,"
|
||||
}
|
||||
// remove the last comma and encode
|
||||
speakerString = encode(speakerString[0..-2])
|
||||
}
|
||||
|
||||
if (playlist) {
|
||||
obything.playTrack(state.sound.uri, speakerString, volume, resumePlaying, playlist)
|
||||
}
|
||||
else {
|
||||
obything.playTrack(state.sound.uri, speakerString, volume, resumePlaying)
|
||||
}
|
||||
|
||||
if (frequency || oncePerDay) {
|
||||
state[frequencyKey(evt)] = now()
|
||||
}
|
||||
}
|
||||
|
||||
private playlistOptions() {
|
||||
def playlistString = obything.currentValue("playlists")
|
||||
log.debug "Playlists are $playlistString"
|
||||
def jsonList = new JsonSlurper().parseText(playlistString)
|
||||
jsonList.collect {
|
||||
it.Name
|
||||
}
|
||||
}
|
||||
|
||||
private frequencyKey(evt) {
|
||||
"lastActionTimeStamp"
|
||||
}
|
||||
|
||||
private dayString(Date date) {
|
||||
def df = new java.text.SimpleDateFormat("yyyy-MM-dd")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
df.format(date)
|
||||
}
|
||||
|
||||
private oncePerDayOk(Long lastTime) {
|
||||
def result = true
|
||||
if (oncePerDay) {
|
||||
result = lastTime ? dayString(new Date()) != dayString(new Date(lastTime)) : true
|
||||
log.trace "oncePerDayOk = $result"
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// TODO - centralize somehow
|
||||
private getAllOk() {
|
||||
modeOk && daysOk && timeOk
|
||||
}
|
||||
|
||||
private getModeOk() {
|
||||
def result = !modes || modes.contains(location.mode)
|
||||
log.trace "modeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getDaysOk() {
|
||||
def result = true
|
||||
if (days) {
|
||||
def df = new java.text.SimpleDateFormat("EEEE")
|
||||
if (location.timeZone) {
|
||||
df.setTimeZone(location.timeZone)
|
||||
}
|
||||
else {
|
||||
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
|
||||
}
|
||||
def day = df.format(new Date())
|
||||
result = days.contains(day)
|
||||
}
|
||||
log.trace "daysOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private getTimeOk() {
|
||||
def result = true
|
||||
if (starting && ending) {
|
||||
def currTime = now()
|
||||
def start = timeToday(starting).time
|
||||
def stop = timeToday(ending).time
|
||||
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
|
||||
}
|
||||
log.trace "timeOk = $result"
|
||||
result
|
||||
}
|
||||
|
||||
private hhmm(time, fmt = "h:mm a")
|
||||
{
|
||||
def t = timeToday(time, location.timeZone)
|
||||
def f = new java.text.SimpleDateFormat(fmt)
|
||||
f.setTimeZone(location.timeZone ?: timeZone(time))
|
||||
f.format(t)
|
||||
}
|
||||
|
||||
private getTimeLabel()
|
||||
{
|
||||
(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
|
||||
}
|
||||
// TODO - End Centralize
|
||||
|
||||
private loadText() {
|
||||
if (location.timeZone || zipCode) {
|
||||
def weather = getWeatherFeature("forecast", zipCode)
|
||||
def current = getWeatherFeature("conditions", zipCode)
|
||||
def isMetric = location.temperatureScale == "C"
|
||||
def delim = ""
|
||||
def sb = new StringBuilder()
|
||||
list(forecastOptions).sort().each {opt ->
|
||||
if (opt == "0") {
|
||||
if (isMetric) {
|
||||
sb << "The current temperature is ${Math.round(current.current_observation.temp_c)} degrees."
|
||||
}
|
||||
else {
|
||||
sb << "The current temperature is ${Math.round(current.current_observation.temp_f)} degrees."
|
||||
}
|
||||
delim = " "
|
||||
}
|
||||
else if (opt == "1") {
|
||||
sb << delim
|
||||
sb << "Today's forecast is "
|
||||
if (isMetric) {
|
||||
sb << weather.forecast.txt_forecast.forecastday[0].fcttext_metric
|
||||
}
|
||||
else {
|
||||
sb << weather.forecast.txt_forecast.forecastday[0].fcttext
|
||||
}
|
||||
}
|
||||
else if (opt == "2") {
|
||||
sb << delim
|
||||
sb << "Tonight will be "
|
||||
if (isMetric) {
|
||||
sb << weather.forecast.txt_forecast.forecastday[1].fcttext_metric
|
||||
}
|
||||
else {
|
||||
sb << weather.forecast.txt_forecast.forecastday[1].fcttext
|
||||
}
|
||||
}
|
||||
else if (opt == "3") {
|
||||
sb << delim
|
||||
sb << "Tomorrow will be "
|
||||
if (isMetric) {
|
||||
sb << weather.forecast.txt_forecast.forecastday[2].fcttext_metric
|
||||
}
|
||||
else {
|
||||
sb << weather.forecast.txt_forecast.forecastday[2].fcttext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def msg = sb.toString()
|
||||
msg = msg.replaceAll(/([0-9]+)C/,'$1 degrees') // TODO - remove after next release
|
||||
log.debug "msg = ${msg}"
|
||||
state.sound = textToSpeech(msg, true)
|
||||
}
|
||||
else {
|
||||
state.sound = textToSpeech("Please set the location of your hub with the SmartThings mobile app, or enter a zip code to receive weather forecasts.")
|
||||
}
|
||||
}
|
||||
|
||||
private list(String s) {
|
||||
[s]
|
||||
}
|
||||
private list(l) {
|
||||
l
|
||||
}
|
||||
Reference in New Issue
Block a user