Skip to content

Instantly share code, notes, and snippets.

@nivw
Created December 3, 2017 20:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nivw/3d37108b338852eaec81fdebdd4dba66 to your computer and use it in GitHub Desktop.
Save nivw/3d37108b338852eaec81fdebdd4dba66 to your computer and use it in GitHub Desktop.
integration with rompr
/**
* 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