Created
December 3, 2017 20:15
-
-
Save nivw/3d37108b338852eaec81fdebdd4dba66 to your computer and use it in GitHub Desktop.
integration with rompr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* based on ObyThing Music | |
* | |
*/ | |
import groovy.json.JsonSlurper | |
metadata { | |
definition (name: "Rompr", namespace: "ngw", author: "Niv") { | |
capability "Music Player" | |
capability "Refresh" | |
capability "Switch" | |
// These strings are comma separated lists of names | |
attribute "playlists", "json_object" | |
// 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 "play" | |
command "pause" | |
} | |
simulator { | |
// TODO: define status and reply messages here | |
} | |
tiles { | |
// Main | |
// state "paused", label:'Paused', action:"music Player.play", icon:"st.Electronics.electronics19", nextState:"playing", backgroundColor:"#ffffff" | |
// state "playing", label:'Playing', action:"music Player.pause", icon:"st.Electronics.electronics19", nextState:"paused", backgroundColor:"#79b821" | |
standardTile("main", "device.status", width: 1, height: 1, canChangeIcon: true) { | |
state "pause", label:'Paused', action:"play", icon:"st.Electronics.electronics19", nextState:"play", backgroundColor:"#ffffff" | |
state "play", label:'Playing', action:"pause", icon:"st.Electronics.electronics19", nextState:"pause", backgroundColor:"#79b821" | |
} | |
// Row 1 | |
standardTile("nextTrack", "device.status", width: 1, height: 1, decoration: "flat") { | |
state "next", label:'', action:"music Player.nextTrack", icon:"st.sonos.next-btn", backgroundColor:"#ffffff" | |
} | |
standardTile("playpause", "device.status", width: 1, height: 1, decoration: "flat") { | |
state "default", label:'', action:"music Player.play", icon:"st.sonos.play-btn", nextState:"playing", backgroundColor:"#ffffff" | |
state "play", label:'', action:"music Player.pause", icon:"st.sonos.pause-btn", nextState:"paused", backgroundColor:"#ffffff" | |
state "pause", label:'', action:"music Player.play", icon:"st.sonos.play-btn", nextState:"playing", backgroundColor:"#ffffff" | |
} | |
standardTile("previousTrack", "device.status", width: 1, height: 1, decoration: "flat") { | |
state "previous", label:'', action:"music Player.previousTrack", icon:"st.sonos.previous-btn", backgroundColor:"#ffffff" | |
} | |
// Row 2 | |
standardTile("switch", "device.switch", width: 1, height: 1, decoration: "flat", canChangeIcon: true) { | |
state "on", label:'On', action:"switch.off", icon:"st.Electronics.electronics14", nextState:"off", backgroundColor:"#ffffff" | |
state "off", label:'Off', action:"switch.on", icon:"st.Electronics.electronics16", nextState:"on", backgroundColor:"#ffffff" | |
} | |
standardTile("status", "device.status", width: 1, height: 1, decoration: "flat", canChangeIcon: true) { | |
state "play", label:'Playing', action:"music Player.pause", icon:"st.Electronics.electronics19", nextState:"pause", backgroundColor:"#ffffff" | |
state "stop", label:'Stopped', action:"music Player.play", icon:"st.Electronics.electronics19", nextState:"play", backgroundColor:"#ffffff" | |
state "pause", label:'Paused', action:"music Player.play", icon:"st.Electronics.electronics19", nextState:"play", backgroundColor:"#ffffff" | |
} | |
standardTile("mute", "device.mute", inactiveLabel: false, decoration: "flat") { | |
state "unmuted", label:"Mute", action:"music Player.mute", icon:"st.custom.sonos.unmuted", backgroundColor:"#ffffff", nextState:"muted" | |
state "muted", label:"Unmute", action:"music Player.unmute", icon:"st.custom.sonos.muted", backgroundColor:"#ffffff", nextState:"unmuted" | |
} | |
// Row 3 | |
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) { | |
state "level", action:"music Player.setLevel", backgroundColor:"#ffffff" | |
} | |
// Row 4 - Disable this for now until we get communication back to hub working | |
// valueTile("currentSong", "device.trackDescription", inactiveLabel: true, height:1, width:3, decoration: "flat") { | |
// state "default", label:'${currentValue}', backgroundColor:"#ffffff" | |
// } | |
// Row 5 | |
standardTile("refresh", "device.status", inactiveLabel: false, decoration: "flat") { | |
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh", backgroundColor:"#ffffff" | |
} | |
main "main" | |
details([ | |
"previousTrack","playpause","nextTrack", | |
"switch","status","mute", | |
"levelSliderControl", | |
// "currentSong", | |
"refresh" | |
]) | |
} | |
} | |
// parse events into attributes | |
def parse(description) { | |
log.debug( "Parsing '$description'") | |
def map = stringToMap(description) | |
if (map.headers && map.body) { //got device info response | |
if (map.body) { | |
def bodyString = new String(map.body.decodeBase64()) | |
log.debug( "body = $bodyString") | |
def slurper = new JsonSlurper() | |
def result = slurper.parseText(bodyString) | |
if (result.containsKey("volume")) { | |
log.debug( "setting volume to ${result.volume}") | |
sendEvent(name: "level", value: result.volume) | |
} | |
if (result.containsKey("state")) { | |
log.debug( "setting state to ${result.state}") | |
sendEvent(name: "state", value: result.state) | |
} | |
if (result.containsKey("file")) { | |
def json = new groovy.json.JsonBuilder(result.file) | |
log.debug "setting file to ${json.toString()}" | |
sendEvent(name: "file", value: json.toString()) | |
} | |
if (result.containsKey("playlist")) { | |
def json = new groovy.json.JsonBuilder(result.playlist) | |
log.debug "setting playlist to ${json.toString()}" | |
sendEvent(name: "playlist",value: json.toString()) | |
} | |
} | |
} | |
} | |
def updateState() { | |
log.debug( "updateState: ${params.message}") | |
} | |
def installed() { | |
// subscribeAction("/subscribe") | |
refresh() | |
} | |
// handle commands | |
def refresh() { | |
log.debug "refreshing" | |
//def address = getCallBackAddress() | |
//sendCommand("subscribe=$address") | |
sendCommand("[]") | |
} | |
def on() { | |
log.debug "Turn-on MPD play" | |
play() | |
} | |
def off() { | |
log.debug "Turn-off play in MPD" | |
pause() | |
} | |
def play() { | |
log.debug( "Executing play" ) | |
sendCommand("[[\"play\"]]") | |
//sendEvent(name: "main", value: "playing", isStateChange: true) | |
} | |
def pause() { | |
log.debug( "Executing pause" ) | |
sendCommand("[[\"pause\"]]") | |
//sendEvent(name: "main", value: "paused", isStateChange: true) | |
} | |
def stop() { | |
log.debug( "Executing 'stop'" ) | |
sendCommand("[[\"stop\"]]") | |
} | |
def nextTrack() { | |
log.debug "Executing 'nextTrack'" | |
sendCommand("[[\"next\"]]") | |
} | |
def setLevel(value) { | |
log.debug "Executing 'setLevel' to $value" | |
sendCommand("volume=$value") | |
} | |
def mute() { | |
log.debug "Executing 'mute'" | |
sendCommand("[[\"disableoutput\",0]]") | |
} | |
def previousTrack() { | |
log.debug "Executing 'previousTrack'" | |
sendCommand("command=previous") | |
} | |
def unmute() { | |
log.debug "Executing 'unmute'" | |
sendCommand("[[\"enableoutput\",0]]") | |
} | |
def setTrack(String uri, metaData="") { | |
log.debug "Executing 'setTrack'" | |
sendCommand("track=$uri") | |
} | |
def resumeTrack() { | |
log.debug "Executing 'resumeTrack'" | |
// TODO: handle 'resumeTrack' command | |
} | |
def restoreTrack() { | |
log.debug "Executing 'restoreTrack'" | |
// TODO: handle 'restoreTrack' command | |
} | |
def playPlaylist(String uri, speakers=null, volume=null, resume=false, restore=false) { | |
log.trace "playPlaylist($uri, $speakers, $volume, $resume, $restore)" | |
def command = "playlist&uri=${uri}" | |
if (speakers) { | |
command += "&speaker=${speakers}" | |
} | |
if (volume) { | |
command += "&volume=${volume}" | |
} | |
if (resume) { | |
command += "&resume" | |
} | |
else if (restore) { | |
command += "&restore" | |
} | |
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 = "playTrack&track=${uri}" | |
if (speakers) { | |
command += "&speaker=${speakers}" | |
} | |
if (volume) { | |
command += "&volume=${volume}" | |
} | |
if (resume) { | |
command += "&resume" | |
} | |
else if (restore) { | |
command += "&restore" | |
} | |
if (playlist) { | |
command += "&playlist=$playlist" | |
} | |
sendCommand(command) | |
} | |
// 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 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 getHostAddress() { | |
// this should be set in two input fields IMHO | |
def parts = device.deviceNetworkId.split(":") | |
def ip = convertIPtoHex(parts[0]) | |
def port = convertPortToHex(parts[1]) | |
return ip + ":" + port | |
} | |
def hubActionResponse(response){ | |
log.debug("Response from host: '${device.deviceNetworkId}'") | |
log.debug("$response") | |
} | |
private sendCommand(command) { | |
def path = "/player/mpd/postcommand.php" | |
def headers = [:] | |
def host = getHostAddress() | |
headers.put("HOST", getHostAddress()) | |
//headers.put("Content-Type", "application/x-www-form-urlencoded") | |
def method = "POST" | |
try { | |
//sendHubCommand(new physicalgraph.device.HubAction([ | |
def hubAction = new physicalgraph.device.HubAction([ | |
method: method, | |
path: path, | |
body: command, | |
headers: headers], | |
host, | |
[callback: "hubActionResponse"] | |
) | |
hubAction.options = [outputMsgToS3:true] | |
log.debug("Print hubAction: $hubAction") | |
hubAction | |
} catch (e) { | |
log.debug(e.message) | |
} | |
} | |
private getPlaylists() { | |
log.debug "in getPlaylists!!!" | |
def path = "/get.html?list=playlists" | |
def headers = [:] | |
headers.put("GET", getHostAddress()) | |
headers.put("Content-Type", "application/x-www-form-urlencoded") | |
def method = "GET" | |
def result = new physicalgraph.device.HubAction( | |
method: method, | |
path: path, | |
headers: headers | |
) | |
result | |
} | |
private getCallBackAddress() | |
{ | |
device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP") | |
} | |
private subscribeAction(path, callbackPath="") { | |
def address = device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP") | |
def parts = device.deviceNetworkId.split(":") | |
def ip = convertHexToIP(parts[0]) | |
def port = convertHexToInt(parts[1]) | |
ip = ip + ":" + port | |
def result = new physicalgraph.device.HubAction( | |
method: "SUBSCRIBE", | |
path: path, | |
headers: [ | |
HOST: ip, | |
CALLBACK: "<http://${address}/obything>", | |
NT: "upnp:event", | |
TIMEOUT: "Second-3600"]) | |
result | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment