Plex Webhooks

I don't see a way to make "playbackType" attribute a rule within RM. I can see "playbackType" being set to either moive/tv depending on the content in the logs but no way to use that info within RM.

Is there a way to turn on/off some virtual switches to indicate content type so we can use it within RM?

Sorry I forgot to reply to this message, if RM doesn’t support then at present no. I would perhaps check in the RM thread though, if it doesn’t exist then it probably should.

Otherwise you have a couple of options, either write an app to handle your use case or wait for me to port mine over.. which I will definitely do, but just moved in to a new house and very busy on that at the moment, will likely be before Christmas though.. or if you feel confident to port it over I can share the ST code with you?

I have your ST code running on ST right now to handle some of content type automations so I'll try to port over but not much experience coding so we'll see how things go.

I have an unpublished version which separates the scene control app to its own app.. I’ll have a little time today so I’ll take a look and have a quick look at porting it over..

Did you ever get a chance to work on this?

No sorry,...but I've just had a go at porting it over.. but I am also unable to test it!

It should* work, but there is a good chance it wont.. So give it a go and send me any logging where there are errors and I'll aim to fix quickly!

Parent

/**
 *  Media Scene
 *
 *  Copyright 2018 Jake Tebbett
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 * 
 * VERSION CONTROL - Media Scene
 * ###############
 *
 *  v1.0 - Initial Release
 *
 */

definition(
    name: "MediaScene",
    namespace: "jebbett",
    author: "Jake Tebbett",
    description: "Control scenes based on media state and type",
    category: "My Apps",
    iconUrl: "https://github.com/jebbett/MediaScene/raw/master/Icons/MediaScene.png",
    iconX2Url: "https://github.com/jebbett/MediaScene/raw/master/Icons/MediaScene.png",
    iconX3Url: "https://github.com/jebbett/MediaScene/raw/master/Icons/MediaScene.png"
)

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
}

def initialize() {
}

preferences {
    page name: "parentPage"
}

def parentPage() {
    dynamicPage(name: "parentPage", title: "Rooms", install: true, uninstall: true, submitOnChange: true) {              
        section {
            app(name: "childapp", appName: "MediaSceneChild", namespace: "jebbett", title: "Create New Room", multiple: true)
    	}
        section {
        	input(name: "debugLogging", type: "bool", title: "Enable Debugging", required: false)
        }
    }
}

Child

/**
 *  Media Scene
 *
 *  Copyright 2018 Jake Tebbett
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 * 
 * VERSION CONTROL - Media Scene
 * ###############
 *
 *  v1.0 - Initial Release
 *
 */

definition(
    name: "MediaSceneChild",
    namespace: "jebbett",
    author: "Jake Tebbett",
    description: "Control scenes based on media state and type",
    category: "My Apps",
    parent: "jebbett:MediaScene",
    iconUrl: "https://github.com/jebbett/MediaScene/raw/master/Icons/MediaScene.png",
    iconX2Url: "https://github.com/jebbett/MediaScene/raw/master/Icons/MediaScene.png",
    iconX3Url: "https://github.com/jebbett/MediaScene/raw/master/Icons/MediaScene.png"
)


def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
}

def initialize() {
  	state.catcherRunning = false
    subscribe(playerDT, "status", PlayerDTCommandRecieved)
}

preferences {
    page name: "childPage"
    //Child Pages
    page name: "pageDoThis"
    page name: "pageWhenThis"
    page name: "pageMediaSettings"
}


def childPage() {
    dynamicPage(name: "childPage", uninstall: true, install: true) {
        section() {
                label title: "Enter Room Name", defaultValue: app.label, required: false
        }
        section ("For This Device"){

            input(name: "playerDT", type: "capability.musicPlayer", title: "Media Player Device", multiple: false, required:false)      
      	}
        section("Lights") {
			input "dimmers1", "capability.switchLevel", title: "Adjust level of these bulbs", multiple: true, required: false, submitOnChange: true
            input "hues1", "capability.colorControl", title: "Adjust level and color of these bulbs", multiple:true, required:false, submitOnChange: true
            if(hues1||dimmers1) {
            input(name: "iLevelOnPlay1", type: "number", title: "Level on Play", defaultValue:0)
            input(name: "iLevelOnPause1", type: "number", title: "Level on Pause", defaultValue:30)
            input(name: "iLevelOnStop1", type: "number", title: "Level on Stop", defaultValue:100)
            }
            if(hues1) {
				input "colorOnPlay", "enum", title: "Hue Bulbs > Color On Play", required: false, multiple: false, submitOnChange: true,
					options: ["Soft White", "White", "Daylight", "Warm White", "Red", "Green", "Blue", "Yellow", "Orange", "Purple", "Pink"]
                input "colorOnPause", "enum", title: "Hue Bulbs > Color On Pause", required: false, multiple: false, submitOnChange: true,
					options: ["Soft White", "White", "Daylight", "Warm White", "Red", "Green", "Blue", "Yellow", "Orange", "Purple", "Pink"]
                input "colorOnStop", "enum", title: "Hue Bulbs > Color On Stop", required: false, multiple: false, submitOnChange: true,
					options: ["Soft White", "White", "Daylight", "Warm White", "Red", "Green", "Blue", "Yellow", "Orange", "Purple", "Pink"]
                input(name: "tempOnPlay", description: "1000..9999", type: "number", range: "1000..9999", title: "Color Temperature on Play (°K)", required: false)
                input(name: "tempOnPause", description: "1000..9999", type: "number", range: "1000..9999", title: "Color Temperature on Pause (°K)", required: false)
                input(name: "tempOnStop", description: "1000..9999", type: "number", range: "1000..9999", title: "Color Temperature on Stop (°K)", required: false)
            }
            input(name: "bDimOnlyIfOn1", type: "bool", title: "Dim bulbs only if they're already on", required: false)
        }
		section("Switches") {
        	input "switches2", "capability.switch", title:"Switches On when Playing", multiple: true, required: false
            input "switches1", "capability.switch", title:"Switches Off when Playing", multiple: true, required: false
            input(name: "bReturnState1", type: "bool", title: "Switches return to original state when Stopped", required: false)
            input(name: "bSwitchOffOnPause1", type: "bool", title: "Switches use Play config when Paused", required: false)
            input(name: "switchOnPlay", type: "bool", title: "Switches only change on 'Play'", required: false)
            paragraph "The below switches do not toggle off when state becomes inactive, ideal for tiggering external App scenes"
            input "mSwitchPlay", "capability.switch", title:"Momentary switch on Play", multiple: true, required: false
            input "mSwitchPause", "capability.switch", title:"Momentary switch on Pause", multiple: true, required: false
            input "mSwitchStop", "capability.switch", title:"Momentary switch on Stop", multiple: true, required: false
            
        }
		section("Modes") {
			input "playMode1", "mode", title: "Mode when playing", required:false
			input "pauseMode1", "mode", title: "Mode when paused", required:false
			input "stopMode1", "mode", title: "Mode when stopped", required:false
		}
        section("Routines") {
        	def actions = location.helloHome?.getPhrases()*.label
        	input "playRoutine", "enum", title: "Routine when playing", required: false, options: actions
            input "pauseRoutine", "enum", title: "Routine when paused", required: false, options: actions
            input "stopRoutine", "enum", title: "Routine when stopped", required: false, options: actions
        }
        section("Media Settings") {	
			input(name: "bTreatTrailersAsPause1", type: "bool", title: "Use pause config for movie trailers", required: false)
            input(name: "stopDelay", type: "number", title: "Delay stop action", required:false, defaultValue:0)
            input(name: "pauseDelay", type: "number", title: "Delay pause action", required:false, defaultValue:0)
		}
        section("Restrictions") {
			input "mediaTypeOk", "enum", title: "Only for media types:", multiple: true, submitOnChange: true, required: false,
			options: ['movie', 'episode', 'clip', 'track']
        	input "disabled", "capability.switch", title: "Switch to disable when On", required: false, multiple: false
            input "activeMode", "mode", title: "Only run in selected modes", multiple: true, required:false
        }
    }
}


// Recieve command from MusicPlayer device type
def PlayerDTCommandRecieved(evt){
	def mediaType = playerDT.currentplaybackType ?: null

	if(evt.value=="playing"){		AppCommandRecieved("onplay", mediaType)}
	else if(evt.value=="stopped"){	AppCommandRecieved("onstop", mediaType)}
    else if(evt.value=="paused"){	AppCommandRecieved("onpause", mediaType)}
}



def AppCommandRecieved(command, mediaType) {

// Stop running if disable switch is activated    
    if (disabled != null) {if(disabled.currentSwitch == "on") {logWriter ("Disabled via switch"); return}}
    if (activeMode != null && !activeMode.contains(location.mode)) {logWriter ("Disabled via invalid mode"); return}

// Check if Media Type is correct
	if(mediaTypeOk){
		def mediaTypeFound = mediaTypeOk.find { item -> item == mediaType}
    	if(mediaTypeFound == null) {logWriter ("Match NOT found for media type: ${mediaType}"); return}
	}
    
//Translate play to pause if bTreatTrailersAsPause is enabled for this room
    if(settings?.bTreatTrailersAsPause1 && mediaType == "clip" && command == "onplay") {command = "onpause"}

// Unschedule delays
	unschedule(StopCommand)
    unschedule(PauseCommand)

// Play, Pause or Stop
    if (command == "onplay") {
    	logWriter ("Playing")
        PlayCommand()
    }
    else if (command == "onpause") {        
        logWriter ("Paused")
        if(!settings?.pauseDelay || pauseDelay == "0"){
        	PauseCommand()
        }else{
            logWriter ("Pause Action Delay")
        	runIn(settings?.pauseDelay.value, PauseCommand)
    	}
    }
    else if (command == "onstop") {
        logWriter ("Stopped")
        if(!settings?.stopDelay || stopDelay == "0"){
        	StopCommand()
        }else{
           	logWriter ("Stop Action Delay")
        	runIn(settings?.stopDelay.value, StopCommand)
        }
    }
}

def PlayCommand(){
	if(!state.catcherRunning){
        catchState("switches1")
    	catchState("switches2")
        state.catcherRunning = true
    }
    if(settings?.playMode1){setLocationMode(playMode1)}
	SetLevels(iLevelOnPlay1, colorOnPlay, tempOnPlay)
    SetSwitchesOff()
    mSwitchPlay?.on()
    if(playRoutine) { location.helloHome?.execute(playRoutine) }
}

def PauseCommand(){
    if(settings?.pauseMode1){setLocationMode(pauseMode1)}
   	SetLevels(iLevelOnPause1, colorOnPause, tempOnPause)
    mSwitchPause?.on()
    if(pauseRoutine) { location.helloHome?.execute(pauseRoutine) }
    if(settings?.bSwitchOffOnPause1) {
   		SetSwitchesOff()
    } else {
       	if(state.catcherRunning && settings?.bReturnState1){
       		returnToState("switches1")
   			returnToState("switches2")
           	state.catcherRunning = false
       	}else{
       		SetSwitchesOn()
           	state.catcherRunning = false
       	}
    }
}

//Stop command
def StopCommand(){

	if(settings?.stopMode1){setLocationMode(settings?.stopMode1)}
    SetLevels(iLevelOnStop1, colorOnStop, tempOnStop)
    mSwitchStop?.on()
    if(stopRoutine) { location.helloHome?.execute(stopRoutine) }
    if(state.catcherRunning && settings?.bReturnState1){
       	returnToState("switches1")
    	returnToState("switches2")
        state.catcherRunning = false
    }else{
       	SetSwitchesOn()
        state.catcherRunning = false
    }
}

// Actions
def SetSwitchesOn() {
	if(!switchOnPlay){
		switches1?.on()
    	switches2?.off()
    }
}
def SetSwitchesOff() {
	switches1?.off()
    switches2?.on()
}

def SetLevels(level, acolor, temp) {
	// If color specified set hues
    if (level != null) {
    	def hueColor = 23
		def saturation = 56
		switch(acolor) {
			case "White":
				hueColor = 52
				saturation = 19
				break;
			case "Daylight":
				hueColor = 53
				saturation = 91
				break;
			case "Soft White":
				hueColor = 23
				saturation = 56
				break;
			case "Warm White":
				hueColor = 20
				saturation = 80 //83
				break;
			case "Blue":
				hueColor = 70
				break;
			case "Green":
				hueColor = 35
				break;
			case "Yellow":
				hueColor = 25
				break;
			case "Orange":
				hueColor = 10
				break;
			case "Purple":
				hueColor = 75
				break;
			case "Pink":
				hueColor = 83
				break;
			case "Red":
				hueColor = 100
				break;
		}
        
        if (settings?.bDimOnlyIfOn1){
        	if(acolor != null){ 	hues1?.each 	{ hue -> if ("on" == hue.currentSwitch) 	{ hue.setColor([hue: hueColor, saturation: saturation, level: level]) } } }
            else if(temp != null){ 	hues1?.each 	{ hue -> if ("on" == hue.currentSwitch) 	{ hue.setColorTemperature(temp) } } }
            else {					hues1?.each 	{ hue -> if ("on" == hue.currentSwitch) 	{ hue.setLevel(level) } } }           
        							dimmers1?.each 	{ bulb -> if ("on" == bulb.currentSwitch) 	{ bulb.setLevel(level) } }
        }else{
        	// color takes priority over temperature, dimmers will still set temperature if available
        	if(acolor != null){		hues1?.setColor([hue: hueColor, saturation: saturation, level: level]) }
            else if(temp != null){	hues1?.setColorTemperature(temp) }
			dimmers1?.setLevel(level)
        }
	}
}

//Save state
private catchState(switches) {
        settings."${switches}"?.each { switcher -> state."${switcher.id}State" = switcher.currentValue("switch")
        	logWriter (switcher.currentValue("switch"))
        }
}
//Return to state
private returnToState(switches) {
	settings."${switches}"?.each {switcher -> 
    	if(state."${switcher.id}State" == "on") {switcher.on()}
        if(state."${switcher.id}State" == "off") {switcher.off()}
    }
}


//// GENERIC CODE

private def logWriter(value) {
    if(parent.debugLogging) {log.debug "Media Scene [${app.label}] >> ${value}"}
}
1 Like

@jpark Let me know how you get on with this, I did give a couple of tests last night and seems to be working!

2 Likes

Working great!

I can finally get rid of my SmartThings!

thanks!

2 Likes

Sorry spoke too soon.

Media Type restriction doesn't seem to be working.
The log says it's NULL no matter what is played.

image

Will take a look.. what method are you using for updates? Webhooks?

Webhooks.

The Plex Communicator device is showing the correct media type in the logs when I play content.

I can't figure out the issue, I can't get the current state for this variable.. I've posted in another thread to see if anyone else has any idea..

Find the latest code updated in the original post below..

@jpark you will need to update the driver for the Plex Communicator Device.. this should fix your problem!

FYI, I just set this up and it is working great. Another thing off of SmartThings!!

1 Like

Would you be willing to point out to me the part of the code that receives the json and parses it? I am trying to d a similar thing with Emby but do not need all of the lighting automation .

It’s in the Plex communicator app under plexWebHookHandler there are two points this is referenced in the code that you’ll need to make it work

I think from a quick check you need to parse playstate.command

1 Like

trying to get this to work. All of my playback devices show up in the app, but nothing shows up in the log when I start or stop playback. I'd love to be able to have the lights turn on and off based on play/pause state.

also, any chance you could make this work with kodi? that's where i'd really love to use it.

this guy seemed to be working on something similar for ST:
Kodi callback endpoint for light control

thanks

You'll need to give me some more info, are you using Plex webhooks, the exe or polling? What are you seeing in live logging?

To implement Kodi would be a whole new app, it's completely different in operation but would be possible.. I'm certain someone already did this in ST

I see now that a plex pass subscription is required to use the webhooks. Using the polling, it says "polling update" in live logging, but doesn't seem to reflect any changes.

Yeah, there are a couple of projects in the ST forums, but I have no idea how to port them over to hubitat (seems to be quite a bit more than just replacing physicalgraph).

It's possible polling isn't working, as I haven't tested it.. the exe is the preferable option if you don't have Plex pass as its a lower overhead for HE.. I'll take a look at polling when I get back home though and see what's going on..

With regards to porting, it's a little bit trial and error, but not too difficult with a little debugging