mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-13 13:21:53 +00:00
This version uses OAuth to simplify the setup process. It also uses child apps to make it easy for users to find the apps that work well with the devices. Please email me when you are testing and I can send a beta version of the Mac app to the tester(s).
212 lines
5.8 KiB
Groovy
212 lines
5.8 KiB
Groovy
/**
|
|
* 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()
|
|
}
|
|
|
|
// 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))
|
|
}
|