Create plex_lifx_color_theme.pyw
This commit is contained in:
parent
0dc0890381
commit
16d91370f1
278
fun/plex_lifx_color_theme.pyw
Normal file
278
fun/plex_lifx_color_theme.pyw
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
#
|
||||||
|
# Author: Bailey Belvis (https://github.com/philosowaffle)
|
||||||
|
#
|
||||||
|
# Will dim your lifx lights and set them to a color theme matching the currently
|
||||||
|
# playing media.
|
||||||
|
#
|
||||||
|
# - Enable `Upload Posters to Imgur for Notifications` - required for lights to match the posters color scheme
|
||||||
|
# - Triggers - PlexLifx supports the following triggers, enable the ones you are interested in.
|
||||||
|
# - Notify on Playback Start
|
||||||
|
# - Notify on Playback Stop
|
||||||
|
# - Notify on Playback Resume
|
||||||
|
# - Notify on Playback Pause
|
||||||
|
#
|
||||||
|
# - Copy paste the following line to each of the Triggers you enabled (found on the Arguments tab):
|
||||||
|
# -a {action} -mt {media_type} -mi {machine_id} -rk {rating_key} -pu {poster_url}
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import hashlib
|
||||||
|
import shutil
|
||||||
|
import numpy
|
||||||
|
import argparse
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from random import shuffle
|
||||||
|
from pifx import PIFX
|
||||||
|
from colorthief import ColorThief
|
||||||
|
|
||||||
|
######################################
|
||||||
|
# Configuration - EDIT THESE SETTINGS
|
||||||
|
######################################
|
||||||
|
|
||||||
|
LogFile = "plex_lifx.log"
|
||||||
|
|
||||||
|
# List of Player Id's that should trigger this script. In order to identify your
|
||||||
|
# players UUID, enter a dummy id, enable the script, and start playing some media
|
||||||
|
# on the target device. In the Tautulli logs, search for `plex_lifx`, the UUID should
|
||||||
|
# appear there.
|
||||||
|
PlayerUUIDs = ""
|
||||||
|
|
||||||
|
# LIFX API Key
|
||||||
|
APIKey = ""
|
||||||
|
|
||||||
|
# Bulb Brightness when media is playing
|
||||||
|
Brightness = .25
|
||||||
|
|
||||||
|
# Transition duration
|
||||||
|
Duration = 3.0
|
||||||
|
|
||||||
|
# Number of colors to be used across your lights
|
||||||
|
NumColors = 5
|
||||||
|
|
||||||
|
# How closely the colors should match the media thumbnail, 10 is the highest
|
||||||
|
ColorQuality = 10
|
||||||
|
|
||||||
|
# Default theme to restore lights to on media pause/stop
|
||||||
|
DefaultPauseTheme = "Basic"
|
||||||
|
|
||||||
|
# Default theme to set lights to when media poster fails
|
||||||
|
DefaultPlayTheme = "Blue"
|
||||||
|
|
||||||
|
# Lights that should be controlled by this script, the order the lights are specified in will effect the order in which colors are applied.
|
||||||
|
# You can play around with different light orders until you find one that spreads the colors how you like. You can also configure more
|
||||||
|
# or fewer colors above (see 'NumColors') in order to increase or decrease the amount of color diversity across lights.
|
||||||
|
Lights = "Corner Lamp,Kitchen Lamp,Standing Lamp 1,Standing Lamp 3,Standing Lamp 2,Bedroom Lamp,Tall Corner Lamp,Titan Lamp"
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# Logging Setup
|
||||||
|
##############################
|
||||||
|
|
||||||
|
logger = logging.getLogger('plex_lifx')
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
# Formatter
|
||||||
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s: %(message)s')
|
||||||
|
|
||||||
|
# File Handler
|
||||||
|
file_handler = logging.FileHandler(LogFile)
|
||||||
|
file_handler.setLevel(logging.DEBUG)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# Console Handler
|
||||||
|
console_handler = logging.StreamHandler()
|
||||||
|
console_handler.setLevel(logging.INFO)
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
logger.debug("Starting Plex+Lifx Script :)")
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# Plex Setup
|
||||||
|
##############################
|
||||||
|
|
||||||
|
filtered_players = [] if PlayerUUIDs == "none" else PlayerUUIDs.split(',')
|
||||||
|
|
||||||
|
logger.debug("Filtered Players: " + filtered_players.__str__())
|
||||||
|
|
||||||
|
events = [
|
||||||
|
'play',
|
||||||
|
'pause',
|
||||||
|
'resume',
|
||||||
|
'stop'
|
||||||
|
]
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# LIFX Setup
|
||||||
|
##############################
|
||||||
|
|
||||||
|
brightness = Brightness if Brightness else .35
|
||||||
|
duration = Duration if Duration else 2.0
|
||||||
|
num_colors = NumColors if NumColors else 4
|
||||||
|
color_quality = ColorQuality if ColorQuality else 1
|
||||||
|
|
||||||
|
if not APIKey:
|
||||||
|
logger.error("Missing LIFX API Key")
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
lifx_api_key = APIKey
|
||||||
|
logger.debug("LIFX API Key: " + lifx_api_key)
|
||||||
|
|
||||||
|
pifx = PIFX(lifx_api_key)
|
||||||
|
|
||||||
|
lights = []
|
||||||
|
if Lights:
|
||||||
|
lights_use_name = True
|
||||||
|
lights = Lights.split(',')
|
||||||
|
|
||||||
|
tmp = []
|
||||||
|
for light in lights:
|
||||||
|
tmp.append(light.strip())
|
||||||
|
lights = tmp
|
||||||
|
else:
|
||||||
|
lights_detail = pifx.list_lights()
|
||||||
|
for light in lights_detail:
|
||||||
|
lights.append(light['id'])
|
||||||
|
shuffle(lights)
|
||||||
|
|
||||||
|
scenes_details = pifx.list_scenes()
|
||||||
|
scenes = dict()
|
||||||
|
for scene in scenes_details:
|
||||||
|
scenes[scene['name']] = scene['uuid']
|
||||||
|
|
||||||
|
logger.debug(scenes)
|
||||||
|
logger.debug(lights)
|
||||||
|
|
||||||
|
default_pause_theme = DefaultPauseTheme
|
||||||
|
default_play_theme = DefaultPlayTheme
|
||||||
|
|
||||||
|
default_pause_uuid = scenes[default_pause_theme]
|
||||||
|
default_play_uuid = scenes[default_play_theme]
|
||||||
|
|
||||||
|
number_of_lights = len(lights)
|
||||||
|
if number_of_lights < num_colors:
|
||||||
|
num_colors = number_of_lights
|
||||||
|
|
||||||
|
light_groups = numpy.array_split(numpy.array(lights), num_colors)
|
||||||
|
|
||||||
|
logger.debug("Number of Lights: " + color_quality.__str__())
|
||||||
|
logger.debug("Number of Colors: " + num_colors.__str__())
|
||||||
|
logger.debug("Color Quality: " + color_quality.__str__())
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# Arg Parser
|
||||||
|
##############################
|
||||||
|
p = argparse.ArgumentParser()
|
||||||
|
p.add_argument('-a', '--action', action='store', default='',
|
||||||
|
help='The action that triggered the script.')
|
||||||
|
p.add_argument('-mt', '--media_type', action='store', default='',
|
||||||
|
help='The media type of the media being played.')
|
||||||
|
p.add_argument('-mi', '--machine_id', action='store', default='',
|
||||||
|
help='The machine id of where the media is playing.')
|
||||||
|
p.add_argument('-rk', '--rating_key', action='store', default='',
|
||||||
|
help='The unique identifier for the media.')
|
||||||
|
p.add_argument('-pu', '--poster_url', action='store', default='',
|
||||||
|
help='The poster url for the media playing.')
|
||||||
|
|
||||||
|
parser = p.parse_args()
|
||||||
|
|
||||||
|
##############################
|
||||||
|
# Script Begin
|
||||||
|
##############################
|
||||||
|
|
||||||
|
event = parser.action
|
||||||
|
media_type = parser.media_type
|
||||||
|
player_uuid = parser.machine_id
|
||||||
|
media_guid = parser.rating_key
|
||||||
|
poster_url = parser.poster_url
|
||||||
|
|
||||||
|
logger.debug("Event: " + event)
|
||||||
|
logger.debug("Media Type: " + media_type)
|
||||||
|
logger.debug("Player UUI: " + player_uuid)
|
||||||
|
logger.debug("Media Guid: " + media_guid)
|
||||||
|
logger.debug("Poster Url: " + poster_url)
|
||||||
|
|
||||||
|
# Only perform action for event play/pause/resume/stop for TV and Movies
|
||||||
|
if not event in events:
|
||||||
|
logger.debug("Invalid action: " + event)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
if (media_type != "movie") and (media_type != "episode"):
|
||||||
|
logger.debug("Media type was not movie or episode, ignoring.")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# If we configured only specific players to be able to play with the lights
|
||||||
|
if filtered_players:
|
||||||
|
try:
|
||||||
|
if player_uuid not in filtered_players:
|
||||||
|
logger.info(player_uuid + " player is not able to play with the lights")
|
||||||
|
exit()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to check uuid - " + e.__str__())
|
||||||
|
|
||||||
|
# Setup Thumbnail directory paths
|
||||||
|
upload_folder = os.getcwd() + '\\tmp'
|
||||||
|
thumb_folder = os.path.join(upload_folder, media_guid)
|
||||||
|
thumb_path = os.path.join(thumb_folder, "thumb.jpg")
|
||||||
|
|
||||||
|
if event == 'stop':
|
||||||
|
if os.path.exists(thumb_folder):
|
||||||
|
logger.debug("Removing Directory: " + thumb_folder)
|
||||||
|
shutil.rmtree(thumb_folder)
|
||||||
|
|
||||||
|
pifx.activate_scene(default_pause_uuid)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
if event == 'pause':
|
||||||
|
pifx.activate_scene(default_pause_uuid)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
if event == 'play' or event == "resume":
|
||||||
|
|
||||||
|
# If the file already exists then we don't need to re-upload the image
|
||||||
|
if not os.path.exists(thumb_folder):
|
||||||
|
try:
|
||||||
|
logger.debug("Making Directory: " + thumb_folder)
|
||||||
|
os.makedirs(thumb_folder)
|
||||||
|
urllib.urlretrieve(poster_url, thumb_path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
logger.info("No file found in request")
|
||||||
|
pifx.activate_scene(default_play_uuid)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
# Determine Color Palette for Lights
|
||||||
|
color_thief = ColorThief(thumb_path)
|
||||||
|
palette = color_thief.get_palette(color_count=num_colors, quality=color_quality)
|
||||||
|
logger.debug("Color Palette: " + palette.__str__())
|
||||||
|
|
||||||
|
# Set Color Palette
|
||||||
|
pifx.set_state(selector='all', power="off")
|
||||||
|
for index in range(len(light_groups)):
|
||||||
|
try:
|
||||||
|
color = palette[index]
|
||||||
|
light_group = light_groups[index]
|
||||||
|
|
||||||
|
logger.debug(light_group)
|
||||||
|
logger.debug(color)
|
||||||
|
|
||||||
|
color_rgb = ', '.join(str(c) for c in color)
|
||||||
|
color_rgb = "rgb:" + color_rgb
|
||||||
|
color_rgb = color_rgb.replace(" ", "")
|
||||||
|
|
||||||
|
for light_id in light_group:
|
||||||
|
if lights_use_name:
|
||||||
|
selector = "label:" + light_id
|
||||||
|
else:
|
||||||
|
selector = light_id
|
||||||
|
|
||||||
|
logger.debug("Setting light: " + selector + " to color: " + color_rgb)
|
||||||
|
pifx.set_state(selector=selector, power="on", color=color_rgb, brightness=brightness, duration=duration)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
|
||||||
|
exit()
|
Loading…
Reference in New Issue
Block a user