Recommendations on HTTP Bindings and Command Mapping

Hopefully the title of my topic is appropriate!

I am looking for some advice on how best to proceed with developing a driver based on HTTP Get methods.

I am not exactly sure what my final use case will be but I wanted to start an Integration with SageTV Media Center.

So lets say I want to duplicate remote control behaviors. Would I write and map individual commands? Or would I write maybe one universal command that processes a variable? I have both type of commands working from the device page but as I continue to build out I wonder which is best for say dashboard use or rule machine.

Universal command (not actual code):

command "Command", ["string"]

def Command(command) {
httpGet("http://ip:port/sage/SageCommand?command=${command}")

Individual Commands:

command "Home"
command "TV"

def Home() {
httpGet("http://ip:port/sage/SageCommand?command=Home")

def TV() {
httpGet("http://ip:port/sage/SageCommand?command=TV")

Thanks
Mike

Is the sending of the http gets to the Sage the only thing you are looking to do? I would think you might be able to do that with Rule Machine . . .

To start yes but then of course there could be more.

As for the HTTP gets, I would also have to include an extender id in the url for where the code should be sent. I currently have 3 extenders in the house.

Down the road I would look into showing upcoming recordings and show currently playing. I believe this will require parsing Html.

Thanks
Mike

Usually when creating an app/driver I throw together a quick pseudo code of what I'm looking to do and then adjust it. Sometimes I see where this would not work and make adjustments before even coding.

In this case I would probably do the following.

Settings:  IP, Port  (this way you can create a different device for each sage install)
Capabilities:  pushableButton
Commands: sendCommand ["string"], push 

Then you can create a remote control in a dashboard and just add a bunch of buttons and make each button execute a different command which then gets processed from the push method.

(off the top of my head putting together ruff code)

def push(btn) {
       sendCommand(btn) // on the dashboard you can put a string in for the button
}

def sendCommand(cmd) {
  httpGet("<urlstuff>&cmd=${cmd}")
}

The only other thing I would recommend is using asynch httpGet commands. Its a lot easier on the system as it doesn't wait around for a response. And later on you can process other things in the callback method for added functionality.

You can take a look at my Fully Kiosk code to get some ideas if needed. Its a very simple driver too that just sends commands the same way.

Hopefully this makes sense and helps. Everybody thinks differently when it comes to thier coding process.

2 Likes

This is great and the kind of guidance I was looking for.

Cheers
Mike

Once again Thanks! So far this has worked great! I am using just the 2 commands to control SageTv with the virtual remote.

My next step will be to see how to incorporate rules and parse html for displaying current playing and upcoming recordings.

Cheers
Mike

Glad it worked out.

For the now playing this is probably how I would attack it.

Create a custom attribute for your device

attribute "nowPlaying"

Then if sage has a call that you can request the currently playing track, you would call it the same way but then parse the response (hopefully it responds in json to make it easy) in the call back method. Again look at my last driver for an example of parsing the response.

Once you have the currently playing track as a string you can just set your custom attribute to it by doing the following

sendEvent([name:"nowPlaying",value:responseNowPlaying])

Now, with all that setup, on your dashboard create an attribute tile and set it to display the nowPlaying attribute and it will show on your dashboard.

The cool thing is you can also include basic html in the nowPlaying and the attribute tile will parse this. Its not the prettiest but you could include a converart image or something extra like that. The only concern is that cloud dashboards either won't have access to the image directly or it may be too big to show on the cloud dashboard.

I am not able to request json at this point. There may be away to do this with a different Sage api but not sure. Here is my request:

def postParams = [
    uri: "http://ip:port/sagex/api/Global?command=GetScheduledRecordings",	
		contentType: "text/xml"
//		contentType: 'application/json'
]
try {
    httpGet(postParams){response ->
        if(response.status != 200) {
            log.error "error: ${response.status}."
        } else {

				  log.trace 'Sage response.data: ' + response.data

Here is part of the response in the log:

dev:70812019-04-01 08:13:10.022 am traceSage response.data: TV1445807trueWCAUDT2 (WCAU-DT2)COZITV1https://s3.amazonaws.com/schedulesdirect/assets/stationLogos/s78851_h3_aa.png460trueWCAUDT245807The Blacklist1555120800000truefalse032480378truefalsefalsefalsetrueActionJames SpaderMegan BooneDiego KlattenhoffAmir ArisonMozhan MarnòHisham TawfiqHarry LennixMary Pat GleasonAl SapienzaMarsha Stephanie BlakeMichael AronovMax CasellaSullivan JonesKatherine WillisLaurence MasonRaphael SbargeRoxanna Hope RadjaAngela JeanneauDaniel Morgan ShelleyMarie Grace LaferraraMaxim SwintonBernie RachelleShpend XaniRenne GjoniKossim OsseniBoma AkporeJon BokenkampJon BokenkampJohn EisendrathJohn DavisJohn FoxBill RoeT CooperAllison Glock-CooperCrime drama / Action / ThrillerEP017386900135James SpaderMegan BooneDiego KlattenhoffAmir ArisonMozhan MarnòHisham TawfiqHarry LennixMary Pat GleasonAl SapienzaMarsha Stephanie BlakeMichael AronovMax CasellaSullivan JonesKatherine WillisLaurence MasonRaphael SbargeRoxanna Hope RadjaAngela JeanneauDaniel Morgan ShelleyMarie Grace LaferraraMaxim SwintonBernie RachelleShpend XaniRenne GjoniKossim OsseniBoma AkporeJon BokenkampJon BokenkampJohn EisendrathJohn DavisJohn FoxBill RoeT CooperAllison Glock-CooperLiz and Ressler investigate a Blacklister who offers to erase gambling debts -- at a deceptively high price; Dembe grows concerned as Red tests loyalties of their business associates in search of the individual who turned him in to the police.0.001555088400000616Crime dramaActionThrillerfalse0Lady LuckThe BlacklistCrime drama0James Spader, Megan Boone, Diego Klattenhoff, Amir Arison, Mozhan Marnò, Hisham Tawfiq, Harry Lennix, Mary Pat Gleason, Al Sapienza, Marsha Stephanie Blake, Michael Aronov, Max Casella, Sullivan Jones, Katherine Willis, Laurence Mason, Raphael Sbarge, Roxanna Hope Radja, Angela Jeanneau, Daniel Morgan Shelley, Marie Grace Laferrara, Maxim Swinton, Bernie Rachelle, Shpend Xani, Renne Gjoni, Kossim Osseni, Boma Akpore, Jon Bokenkamp, Jon Bokenkamp, John Eisendrath, John Davis, John Fox, Bill Roe, T Cooper, Allison Glock-CoopertrueActorActorActorActorActorActorActorGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarGuest StarCreatorExecutive ProducerExecutive ProducerExecutive ProducerExecutive ProducerDirectorWriterWriterJames Spader as Raymond ``Red'' ReddingtonMegan Boone as Elizabeth KeenDiego Klattenhoff as Donald ResslerAmir Arison as Aram MojtabaiMozhan Marnò as Samar NavabiHisham Tawfiq as Dembe ZumaHarry Lennix as Harold CooperMary Pat GleasonAl SapienzaMarsha Stephanie BlakeMichael AronovMax CasellaSullivan JonesKatherine WillisLaurence MasonRaphael SbargeRoxanna Hope RadjaAngela JeanneauDaniel Morgan ShelleyMarie Grace LaferraraMaxim SwintonBernie RachelleShpend XaniRenne GjoniKossim OsseniBoma AkporeJon BokenkampJon BokenkampJohn EisendrathJohn DavisJohn FoxBill RoeT CooperAllison Glock-Cooper4603600000WCAUDT20000155511720000001555120800000360000015551172000001555117200000Closed Captioned, Stereo, DD5.1, LetterboxfalseTV14falseStereoCCDD5.1LetterboxNew11truefalse

Here is a similiar request from the browser:
image

Groovy.util.XmlParser and Groovy.util.XmlSlurper can make short work of that XML response. :slight_smile:

1 Like

So which method is the proper/better way to do this?
I tagged @patrick since he might know who best to answer this.

Method 1:
def result = []
for (i=0; i <4; i++) {
result << [response.data.Airing[i].Show.ShowTitle, response.data.Airing[i].Show.ShowEpisode]
sendEvent(name: "upcomingRecordings", value: result, isStateChange: true)
}

Method 2 (Not sure how to build array here):
response.data.Airing.Show.each { Show ->
def list = []
def show = ${Show.ShowTitle.text()}: ${Show.ShowEpisode.text()}"
list << (show);
sendEvent(name: "upcomingRecordings", value: list, isStateChange: true)
}

Attribute from Device Page
Method 1
upcomingRecordings : [[This Is Us, Her], [The Blacklist, Olivia Olson], [Game Shakers, Hot Bananas], [Knight Squad, Two Wrongs Don't Make a Knight]]
Method 2
upcomingRecordings : [Knight Squad: Two Wrongs Don't Make a Knight]

If both result in the same thing, then ends justify the means :slight_smile: As for efficiency, I'm not an expert as to which is more expensive memory wise.

I would recommend moving this to async http calls so not to pause the execution until the response comes in, especially if that XML response is large.

Thanks! Yeah the async calls was also recommended by @gavincampbell so I will work on sorting that out next.

@gavincampbell Hey Gavin! So just to circle back to this insightful post. How would I keep this updated? Since I am using HTTP and not monitoring Sage for events.

Should I look into using refresh capability or poll? If I use my new interface then I could tie the update to the push command but that would not cover a lot of instances.

Worst case I just put a refresh button on the UI, lol!

A number of ways you can handle this.

  1. I don't know if sage has any sort of call backs so if an event occurs it could trigger an event on hubitat. This would require research.

  2. Not sure if sage has any sort of websockets you can use to monitor changes (like how its done in the harmony hub driver). This would require research.

  3. Polling. Probably the easiest but not most efficient.

Polling requires you to create a method that executes every x seconds (depending on how fast you want the updates to occur). This method could do the refresh so that you can get the info you want from sage and update it in your driver. Other things you need to keep in mind with polling includes

  • restarting when the hub reboots (can be done with the initialize method and capability)
  • watcher schedule that ensures it's running if it stops for whatever reason (just schedule something to kick it every 5 mins)
  • don't schedule it to run too often as this adds load on the hub (especially if not using async)

I'm thinking you will have to go with 3. Just threw 1 and 2 in there in case they have those methods. 1 would be a little more advanced, 2 is pretty reliable/efficient and 3 is the easiest.

1 Like

Thanks a ton @gavincampbell ! This gives me a lot of ways to looking into things. You have been extremely helpful!

Nice. Looks like its coming along great.

I was working on a virtual keypad app and ran into the same issue of a matching 0 icon. :slight_smile:

1 Like