SmartThings XBMC Home Theater Integration

I’m wondering if anyone has dabbled with integrating control of their lights/switches with XBMC? “Wouldn’t it be smart if your lights automatically dimmed when you start watching a movie, and then brighten when you pause the movie to make popcorn?” - gratuitous SmartThings plug.

I previously had the (unreliable) functionality with a similar X10 plugin, but there does not appear to be anything for z-wave devices. From my limited programming knowledge, I have researched and found that xbmc 12.0 and greater can be controlled via json GET and REST commands, but I’m unsure on where to start.

I have found that the command below would execute a play/pause, but am unsure on how to pull that info into a SmartApp.
http://enter.your.ip.here/jsonrpc?request={“jsonrpc”: “2.0”, “method”: “Player.PlayPause”, “params”: { “playerid”: 0 }, “id”: 1}

If anyone has any pointers on working with json and SmartThings, it would be greatly appreciated!

1 Like

Responding to you to subscribe, give you a bump, and tell you that this is a great idea! I’m hoping someone can help out and get this rolling!! Would love to be a tester once we get to that point.

It sounds like it may make more sense to have have XBMC (or rather, whatever is running XBMC) control Smartthings. This can be done pretty easily via REST endpoints.

What do you use to run XBMC? What do you use to control XBMC?

I personally am running it on a jailbroken Apple TV 2.

@Jeff - Appreciate the support, glad to see I have fellow HTPC enthusiasts on the ST forums!

@impliciter - Thanks for the reply. I am running XBMC Frodo 12.2 on a Windows 7 machine. I control XBMC with a Logitech Harmony One remote paired with MCE receiver and EventGhost.

Thanks for the reply. I am running XBMC Frodo 12.2 on a Windows 7 machine. I control XBMC with a Logitech Harmony One remote paired with MCE receiver and EventGhost.

I have a similar setup. My frontend is Plex running on a Raspberry Pi. Control is easy because it supports HDMI-CEC. I have a windows server running Plex backend as well as Eventghost. I also have a MCE Receiver running to the living room with a few blasters and have integrated that “loosly” into smartthings. I created a custom device type in Smartthings that can turn my TV/Receiver On/Off as well as adjust the volume. This works by having the Smartthings device make HTTP Get calls to an Eventghost webserver and then have those events trigger IR blasting. It also works the other way. I can control Smartthings (lights) via the IR remote by capturing the IR events in eventghost and making a HTTP Get to Smartthings.

It sounds quite complicated but it only took an hour or two to setup.

This seems like exactly what I am looking to accomplish! Would you be willing to share the code for the custom device type and/or the xml save file for EventGhost? I am very interested in mimicking your setup, but I do not have any knowledge on HTTP Get calls or what plugins to enable in Eventghost to get all of this working with SmartThings.

If I could get a general grasp on the process of getting XBMC/EventGhost talking with SmartThings, I would be more than willing to write up a tutorial to assist others in need.

What you are doing with Eventghost is sort of like creating an API for your computer. The way I accomplished this was to use the Webserver plugin:
http://www.eventghost.org/mediawiki/index.php?title=Webserver

You can pass commands to eventghost via HTTP Get commands in Smartthings. For example,
httpGet(“http://ipaddress:port/index.html?pwrOn”)
will send create and event ‘http.pwrOn’ in eventghost which you can have trigger some macro, in my case a IR Blast.

The full HTTP Get command I’m using in my custom device is this:

def on() {
	log.debug "Executing 'on'"
    def url = "http://ipaddress:port/index.html?pwrOn"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

One thing to note is that you will need to use your router’s IP address and forward the port to your local machine for Smartthings to have access. It would be nice if this could be done all from within your LAN.

One feature I would like to add is tracking of device status, ie whether the TV/Receive is on or off.

Home Theater Device

Thanks for the help, @impliciter. I’m gonna see if I can slap something together later on. Do you know if much would differ in using json commands with the supplied code above? See below for example -

def PlayPause() {
	log.debug "Executing 'PlayPause'"
    def url = "http://enter.ip.address.here/jsonrpc?request={“jsonrpc”: “2.0″, “method”: “Player.PlayPause”, “params”: { “playerid”: 0 }, “id”: 1}"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

@Cassidy

I don’t think that will work as is. I’m really not familiar with this stuff but I think you will need to encode the URL. I don’t think it gets done automatically and you can’t have spaces and quotes in the URL.

@impliciter

Thank you, I have figured out how to pass httpget from SmartThings to EventGhost so once I get my IR emitters in the mail I will be able to replicate your functionality of turning on/off the TV & receiver via SmartThings, but I am having difficulties with the opposite scenario of sending EventGhost httpget to SmartThings. I would like to be able to press a button on my Logitech remote that would turn the lights on/off. Would you be willing to share a sample of your code for the custom device type you created?

Thanks a lot for all your help!

Have you successfully created REST endpoints for your smartapp(s)?

If so, you should be able to create a python script macro in Eventghost that looks something like this:

import urllib2
urllib2.urlopen("http://example.com/foo/bar").read()

Then, drag your IR event from the log to that macro.

Also, how do you “mention” a user on this forum? I thought the @ would get parsed automatically.

@impliciter

Looks like the REST endpoints was another piece missing from the puzzle.
I borrowed the code for the REST endpoints from http://build.smartthings.com/blog/tutorial-creating-a-custom-rest-smartapp-endpoint/ , works like a charm!

I now am able to turn on/off lights with what I believe is Httpget (see code below), but it does not appear that I can use the setLevel command to enter in a value to dim the lights.

Turn switch off

http://graph.api.smartthings.com/api/smartapps/installations/XXXXXXXX-XXXX-XXXXXXX-XXXXXXXX/switches/enter_switch_id_here/off?access_token=enter_access_token_here

The setLevel command is listed as one of the available commands in the error output though (see error output below)

{“error”:true,“type”:“java.lang.IllegalArgumentException”,“message”:“Command ‘setLevel50’ is not supported. Supported commands: [setLevel, poll, on, off, refresh, indicatorWhenOn, indicatorWhenOff, indicatorNever]”}

I have tried using the following with no luck -

setLevel50
setLevel(50)
setLevel(“50%”)
setLevel.50

Do you know the correct syntax to get the dim functionality working?

Also, how do you “mention” a user on this forum? I thought the @ would get parsed automatically.

I think the mention wasn’t working for my name earlier because my “Display Name” was different than my Username.

I now am able to turn on/off lights with what I believe is Httpget (see code below), but it does not appear that I can use the setLevel command to enter in a value to dim the lights.

Go back and look at the code you ran to setup the endpoints. You are mapping HTTP endpoints to a command, so, only what you have mapped is available.

For example, you mapped

mappings {
    path("/switches") {
        action: [
            GET: "listSwitches"
        ]
    }

    path("/switches/:id/:command") {
        action: [
            GET: "updateSwitch"
        ]
    }
    ...
}

So when you request http://graph.api.smartthings.com/api/smartapps/installations/XXXXXXXX-XXXX-XXXXXXX-XXXXXXXX/switches/enter_switch_id_here/off, it runs the “updateSwitch” command.

You have to figure out how to map an endpoint to a dim command. I have not done this yet but I suppose you could use the same format and do an “if :command is an integer between 0 and 100, setLevel(:command)”.

@impliciter - I am attempting to reproduce your Home Theater device type, and I’m hitting a snag when trying to send button pushes for Volume Up and Volume down.

The device type settings are as follows-
capabilities:
button, momentary, polling, refresh, switch

custom commands:
volup, voldown

The on/off switch works as expected, but no httpget events are generated when I press the Volume Up or Volume down buttons. I feel like it is something simple that I have incorrect in the syntax, but I’m unable to pinpoint it.

/**
 *  Home Theater Control
 *  
 *  code borrowed from @impliciter
 *  Date: 2014-02-23
 */
 // for the UI
metadata {
    tiles {
        standardTile("power", "device.switch", width: 2, height: 2) {
            state "off", label: 'Off', action: "switch.on", icon: "st.Electronics.electronics18", backgroundColor: "#ffffff", nextState: "on"
            state "on", label: 'On', action: "switch.off", icon: "st.Electronics.electronics18", backgroundColor: "#79b821", nextState: "off"
        }
		standardTile("volup", "device.momentary", width: 1, height: 1) {
			state "volup", label: "Vol Up",action: "momentary.push", icon: "st.thermostat.thermostat-up", backgroundColor: "#ffffff"
		}
		standardTile("voldown", "device.momentary", width: 1, height: 1) {
			state "voldown", label: "Vol Down", action: "momentary.push", icon: "st.thermostat.thermostat-down", backgroundColor: "#ffffff"
		}
        main(["power"])
        details(["power", "volup", "voldown"])
    }
}

// parse events into attributes
def parse(String description) {
    log.debug "Parsing '${description}'"
}

// handle commands
def on() {
	log.debug "Executing 'on'"
    def url = "http://ip:port/index.html?pwrOn"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

def off() {
	log.debug "Executing 'off'"
    def url = "http://ip:port/index.html?pwrOff"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

def volup() {
push()
	log.debug "Turning up volume"
    def url = "http://ip:port/index.html?volup"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}
def voldown() {
push()
	log.debug "Turning down volume"
    def url = "http://ip:port/index.html?voldown"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

How did you setup your volume up/down tiles, are they used with the button capability or the momentary capability?

Here is what I have for the metadata. I used a “button” for the volume tiles.

 // for the UI
metadata {
	simulator {
		// TODO: define status and reply messages here
	}
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
			state "off", label: 'Off', action: "pwrOn", icon: "st.Electronics.electronics15", backgroundColor: "#ffffff", nextState: "on"
			state "on", label: 'On', action: "pwrOff", icon: "st.Electronics.electronics15", backgroundColor: "#79b821", nextState: "off"
		}
		standardTile("muteSwitch", "device.switch", canChangeIcon: true) {
			state "mute", label: 'Mute', action: "mute", icon: "st.Electronics.electronics15", backgroundColor: "#ffffff", nextState: "unmute"
			state "unmute", label: '', action: "mute", icon: "st.Electronics.electronics15", backgroundColor: "#79b821", nextState: "mute"
		}
        standardTile("lUp", "device.button", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
                        state "up", label:'Volume Up', action:"volUp",icon:"st.Electronics.electronics14"
        }
        standardTile("lDown", "device.button", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
                        state "down", label:'Volume Down', action:"volDown",icon:"st.Electronics.electronics14"
        }
		main "switch"
		details (["switch","muteSwitch","lUp","lDown",])
	}
}

Chiming in here to say +1 to all this, but even better if the code is abstracted enough that it works with Plex.

@differentcomputers

How do you want to control Plex via Smartthings. It would be pretty trivial to write a device type for a plex client but I haven’t been able to find a use case where I need smartthings to control Plex.

I suppose you could have a doorbell pause a Plex player. What did you have in mind?

I’m thinking of the other way around–having Plex let Smartthings know it’s doing something.

Start a movie in Plex? dim some lights. That sort of thing.

But now that I think about it, it’s much more IR AV control than Plex. I suppose I’m less sensitive to the whole “sense on/off state” issue. I would just love a SmartThings Movie mode that turns all my AV components (including airplay speakers!) to the settings I want: audio source, video source, volume, speaker output selection.

@differentcomputers

You’re right. That would need a combination of an IR blaster and EventGhost, which is what @impliciter guided me through setting up. When I switch to my Movie Mode scene, my TV turns on, receiver turns to the appropriate input, and lights in the family room turn off. If you have an IR blaster like this, the setup is relatively easy.

Here is the Device Type layout
TV Device Type

Here is the device type code with my external IP address replaced with Enter.IP.Address.Here

/**
 *  Home Theater Control
 *
 *
 * Enable the following capabilities for the Device Type - button, switch, refresh
 *
 * Add the following custom commands - volUp, volDown, Input4, Input5, Input6, mute, unmute
 *
 *  Date: 2014-02-24
 */
 // for the UI
metadata {
    tiles {
        standardTile("power", "device.switch", width: 2, height: 2) {
            state "off", label: 'Off', action: "switch.on", icon: "st.Electronics.electronics18", backgroundColor: "#ffffff", nextState: "on"
            state "on", label: 'On', action: "switch.off", icon: "st.Electronics.electronics18", backgroundColor: "#79b821", nextState: "off"
        }
		standardTile("muteSwitch", "device.switch", canChangeIcon: true) {
			state "mute", label: 'Mute', action: "mute", icon: "st.alarm.beep.beep", backgroundColor: "#ffffff", nextState: "unmute"
			state "unmute", label: '', action: "unmute", icon: "st.secondary.off", backgroundColor: "#79b821", nextState: "mute"
		}
        standardTile("lUp", "device.button", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
            state "up", label:'Volume Up', action:"volUp",icon:"st.thermostat.thermostat-up"
        }
		standardTile("lDown", "device.button", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
            state "down", label:'Volume Down', action:"volDown",icon:"st.thermostat.thermostat-down"
        }
        standardTile("Input4", "device.button", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
            state "Input4", label:'Xbox One', action:"Input4",icon:"st.Electronics.electronics5"
        }
		standardTile("Input5", "device.button", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
            state "Input5", label:'XBMC', action:"Input5",icon:"st.Entertainment.entertainment1"
        }
		standardTile("Input6", "device.button", inactiveLabel: false,decoration: "flat", canChangeIcon: false) {
            state "Input6", label:'TV', action:"Input6",icon:"st.Electronics.electronics15"
        }
		main(["power"])
        details(["power", "lUp", "lDown", "Input6", "Input5", "Input4", "muteSwitch"])
    }
}

// handle commands
def on() {
	log.debug "Executing 'on'"
    def url = "http://Enter.IP.Address.Here:80/index.html?pwrOn"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

def off() {
	log.debug "Executing 'off'"
    def url = "http://Enter.IP.Address.Here:80/index.html?pwrOff"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}
def mute() {
	log.debug "Muting the audio"
    def url = "http://Enter.IP.Address.Here:80/index.html?muteOn"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

def unmute() {
	log.debug "Unmuting the audio"
    def url = "http://Enter.IP.Address.Here:80/index.html?muteOff"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}
def volUp() {
	log.debug "Turning up volume"
    def url = "http://Enter.IP.Address.Here:80/index.html?volup"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}
def volDown() {
	log.debug "Turning down volume"
    def url = "http://Enter.IP.Address.Here:80/index.html?voldown"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}
def Input4() {
	log.debug "Changing input to 'Input 4'"
    def url = "http://Enter.IP.Address.Here:80/index.html?Input4"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}
def Input5() {
	log.debug "Changing input to 'Input 5'"
    def url = "http://Enter.IP.Address.Here:80/index.html?Input5"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}
def Input6() {
	log.debug "Changing input to 'Input 6'"
    def url = "http://Enter.IP.Address.Here:80/index.html?Input6"
    httpGet(url) { 
        response -> 
        if (response.status != 200 ) {
            log.debug "Eventghost webserver failed, status = ${response.status}"
        }
    }
}

I just hardcoded my IP into the device type.

You can replace Input4, Input5, and Input6 with whatever you would like to accomplish, unsure on how to enable airplay speakers though, but if they take IR commands it should be a breeze.

Then EventGhost is where the real magic happens. You have to enable the webserver EventGhost plugin, I used port 80 as the port assignment by default. You have to enable port forwarding in your router for port 80 to your HTPC, and then SmartThings will send httpget requests to the EventGhost webserver which will be seen as triggers in EventGhost. Those triggers can then be mapped to actions. So in your case you could map an EventGhost trigger that fires off the “Start Application” action and have it start Plex. The IR blasts to your AV equipment can be recorded as Pronto codes by EventGhost, and can be triggered as actions.

I do not see a Plex plugin available for EventGhost, but you could always send keypresses as well.

2 Likes