Plex + Webhooks (WIP)


#1

Now that plex has webhooks, and they actually work with the new webhook automation I thought I’d share a bit of progress I’ve made, and hope others can chip in!

Firstly you need to have a plex plus subscription - then you need to go into your settings, and webhooks. Add a webhook in the following format:

https://yourserver/api/webhook/someuniqueid

Now add an automation as follows

automation:
  - alias: Plex Webhook
    initial_state: 'on'
    trigger:
      platform: webhook
      webhook_id: someuniqueid
    action:
     - service: mqtt.publish
       data_template:
         topic: 'plex/update'
         payload_template: >
           {{ (trigger.data['payload'] | string)[12:][:-2] | replace ("\\\\", "\\") | replace ("\\\'", "'") | replace ("\\x","?") }}

This will convert the data that comes in into json - sort of. It is still a string, and I haven’t figured out a way to directly convert it to json, so to achieve that, I then push the data into a mqtt queue

I then create an automation based on the topic above, and now I can use trigger.payload_json to get all the data! I push this into a script:

  - alias: Plex Webhook As Json
    initial_state: 'on'
    trigger:
      platform: mqtt
      topic: 'plex/update'
    action:
      - service: script.process_plex_message
        data_template:
          event: "{{ trigger.payload_json.event }}"
          player: "{{ trigger.payload_json.Player.title }}"
          account: "{{ trigger.payload_json.Account.title }}"

The script can then do whatever you want (with conditions)

script:
  process_plex_message:
    sequence:
      - service: notify.slackbot
        data_template:    
          message: "Plex event: {{ event }} - {{ account }} - {{ player }}"

So this is where I’m up to. This works great, Unicode stuff gets escape in a bit of a nasty way, it it does appear to handle it now

I know using a mqtt like this is awful, so any other ideas would be great!

You can see where I’m up to here: https://github.com/rossdargan/hass-config/blob/master/configuration/packages/plex.yaml


#2

I wanted to add my comments that were made in the Reddit thread.

I have a working version of something similar. Plex Webhook to dim my lights with additional conditions, but it’s not a great one. I filter on the Plex username, the Plex Player, and the playback state sent by the webhook. I control my lights with an input boolean of Plex’s current state. If it’s toggled, I check a bunch of other things (like time of day) to see if it’s appropriate to change my lights.

Again, mine parses the string. It’s not a great solution for anyone following, but enough to get it up this weekend. The proper way should be to access the native JSON Payload like how @rossdargan is doing it.

Here’s my automation YAML.
I filter on condition first so I don’t get any errors in my service_template.

- id: '1539522762000'
  alias: Plex State
  trigger:
    platform: webhook
    webhook_id: !secret plex_webhook
  condition:
    condition: and
    conditions:
    - condition: template
      value_template: '{{ "PLEX_USERNAME".encode() in trigger.data.payload }}'
    - condition: template
      value_template: '{{ "PLEX_PLAYER".encode() in trigger.data.payload }}'
  action:
    service_template: >
        {% if (("media.play".encode() in trigger.data.payload) or ("media.resume".encode() in trigger.data.payload)) %}
          input_boolean.turn_on
        {% elif (("media.pause".encode() in trigger.data.payload) or ("media.stop".encode() in trigger.data.payload)) %}
          input_boolean.turn_off
        {% endif %}
    entity_id: input_boolean.plex

#3

Awesome work! I have been testing with notifications and seems to be working well


#4

The next thing I’m working on is seeing if I can use the mqtt sensor to represent a plex player. I need to find someway to have the single json encoded message routed to a topic per player… that is proving tricky!

Glad it’s working for you!


#5

And now I have a sensor that will show True if something is playing on a specific player:

The original automation that pushes the webhook data into a mqtt

automation:
  - alias: Plex Webhook
    initial_state: 'on'
    trigger:
      platform: webhook
      webhook_id: !secret plex_webhook
    action:
     - service: mqtt.publish
       data_template:
         topic: 'plex/update'
         payload_template: >
           {{ (trigger.data['payload'] | string)[12:][:-2] | replace ("\\\\", "\\") | replace ("\\\'", "'") | replace ("\\x","?") }}

This is an automation that will see if the player is for the office (and is not a scrobble, or a rate event) then it will add a message to the topic plex/update/office. This also transforms the json from plex into something a little smaller.

  - alias: Plex Office
    initial_state: 'on'
    trigger:
      platform: mqtt
      topic: 'plex/update'
    condition:
      condition: and
      conditions:
        - condition: template
          value_template: "{{trigger.payload_json.Player.title == 'dar-pc-01'}}"
        - condition: template        
          value_template: "{{trigger.payload_json.event != 'media.scrobble'}}"          
        - condition: template
          value_template: "{{trigger.payload_json.event != 'media.rate'}}"                    
    action:
      - service: mqtt.publish
        data_template:
          topic: 'plex/update/office'
          retain: true
          payload_template: >
           {  "Event":"{{ trigger.payload_json.event }}", 
              "Account":"{{ trigger.payload_json.Account.title }}", 
              "Title":"{{ trigger.payload_json.Metadata.grandparentTitle }}", 
              "Type":"{{ trigger.payload_json.Metadata.librarySectionType }}", 
              "Image":"{{ trigger.payload_json.Metadata.thumb }}" }

This is the sensor. This will have a state of “True” if it is playing something. The attributes will show a bit of other data such as the title of whats playing, or the name of the user playing it.

sensor:
  - platform: mqtt
    name: "Office Plex"
    state_topic: "plex/update/office"    
    value_template: '{{ value_json.Event == "media.resume" or value_json.Event == "media.play" }}'  
    json_attributes:
      - Account
      - Title
      - Type
      - Image
      - Event

Any ideas for improving this would be great!


#6

Great work on this. It would be great if we could see the data passed to HA in the webhook easily. And it would be even better if we could define webhook sensors, saving you a lot of this hassle!


#7

@rossdargan
I just realized what you were trying to do with the mqtt server. I like it a lot! It’s unfortunate that it needs to be parsed like that, but it’s a much better approach than mine to get the json contents.

I used it almost entirely to change my light automation as such:

- id: '1539725480000'
  alias: Plex Webhook MQTT
  trigger:
    platform: webhook
    webhook_id: !secret plex_webhook
  action:
   - service: mqtt.publish
     data_template:
       topic: 'plex/update'
       payload_template: >
         {{ (trigger.data['payload'] | string)[12:][:-2] | replace ("\\\\", "\\") | replace ("\\\'", "'") | replace ("\\x","?") }}
- id: '1539522762000'
  alias: Plex State Toggle
  trigger:
    platform: mqtt
    topic: 'plex/update'
  condition:
    condition: and
    conditions:
    - condition: template
      value_template: "{{trigger.payload_json.Account['title'] == 'PLEX_USER' }}"
    - condition: template
      value_template: "{{trigger.payload_json.Player['uuid'] == 'PLAYER_UUID' }}"
    - condition: template
      value_template: "{{trigger.payload_json.event != 'media.scrobble'}}"
    - condition: template
      value_template: "{{trigger.payload_json.event != 'media.rate'}}" 
  action:
    service_template: >
        {% if ((trigger.payload_json.event == 'media.play') or (trigger.payload_json.event == 'media.resume')) %}
          input_boolean.turn_on
        {% elif ((trigger.payload_json.event == 'media.pause') or (trigger.payload_json.event == 'media.stop')) %}
          input_boolean.turn_off
        {% endif %}
    entity_id: input_boolean.plex

#8

In my GitHub repo you can see where I commented out a notification that sends the contents of the payload to slack. You could easily do something similar. To be honest the docs from Plex do cover pretty well what gets sent


#9

Thanks, yes, I just mean a more general improvement to how webhooks work in HA, not a criticism of your work!


#10

Thanks for this post. I found it helpful for using the media.pause to trigger an event. What I quickly found out, though, is that Plex sends that same event for fast forwarding. I have so far not been able to find a way to differentiate the fast forwarding from the pause, and I was wondering if anyone else reading this post was encountering the same thing and had any ideas?


#11

Yea, doesn’t look possible. I did a diff of the webhook data and these were the only values that changed (fast-forward vs pause). Neither of them look useful to identify which action occurred.

"lastViewedAt": 1544067831,
"viewOffset": 326284,

#12

Finally getting round to working on this. So, am I right in my understanding that the Plex JSON isn’t read properly as JSON?

I managed to get other webhook JSON things working without an issue, but this doesn’t seem to work the same way.


#13

I now have the Plex JSON coming through as JSON through MQTT.

I would like to have all the attributes such as event and sub-attributes come through as “JSON attributes” (see: https://www.home-assistant.io/components/sensor.mqtt/ )

Any ideas on the right format to get the secondary attributes coming through under the JSON attributes section?

I’ve tried, e.g.:
Account.id
Account[id]
Account.[id]
Account[0].id


#14

In appdaemon:

            mydict = json.loads(str(data['payload']))
            user = mydict['Account']['title']
            event = mydict['event']

#15

Thanks. I don’t have app daemon. Will look into it but would prefer not to use anything extra like node red etc (or appdaemon). Will look into it.


#16

Thanks for this project. The package works great exept i get warnings for invalid authentication. Anyone else who has this issue?

I have two-factor auth enabled.