Skip to content
This repository has been archived by the owner on May 26, 2022. It is now read-only.

Commit

Permalink
Dev (#98)
Browse files Browse the repository at this point in the history
* Cleanup and move code to helper functions

* Update helpers.py

* simplify run_start_script

* scan for clients on call

* Update README.md

* remove responding check

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md
  • Loading branch information
maykar committed Feb 19, 2021
1 parent 4d3cc1e commit 984fdb7
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 145 deletions.
42 changes: 33 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,15 +240,39 @@ This option will trigger a script to start a Plex client if it is currently unav
```
"LivingRoom TV":"script.start_lr_plex", "Bedroom TV":"script.open_br_plex"
```
The script would be different for every device and some devices might not have the ability to do this.<br>
An example of a script that would start the Plex app on a Roku device:
The script would be different for every device and some devices might not have the ability to do this.<br><br>
The example below would start the Plex app on a Roku device.<br>The script checks that both the app is open on the device and the app reports as available (take note of the comments in the code).

```
start_lr_plex:
roku_plex:
sequence:
- condition: template
value_template: "{{ state_attr('media_player.roku', 'source') != 'Plex - Stream for Free' }}"
- service: media_player.select_source
entity_id: media_player.roku
data:
source: Plex - Stream for Free
- choose:
#### If Plex is already open on the device, do nothing
- conditions:
- condition: template
value_template: >-
{{ state_attr('media_player.roku','source') == 'Plex - Stream for Free' }}
sequence: []
default:
#### If Plex isn't open on the device, open it
#### You could even add a service to turn your TV on here
- service: media_player.select_source
entity_id: 'media_player.roku'
data:
source: 'Plex - Stream for Free'
- repeat:
#### Wait until the Plex App/Client is available
while:
- condition: template
#### Loop until Plex App or client report as available and stop after 20 tries
value_template: >-
{{ (state_attr('media_player.roku','source') != 'Plex - Stream for Free' or
is_state('media_player.plex_plex_for_roku_roku', 'unavailable')) and
repeat.index <= 20 }}
sequence:
#### Scan to update device status
- service: plex.scan_for_clients
- delay:
seconds: 1
mode: single
```
155 changes: 27 additions & 128 deletions custom_components/plex_assistant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,29 @@
from homeassistant.core import Config, HomeAssistant
from homeassistant.components.plex.services import get_plex_server
from homeassistant.components.zeroconf import async_get_instance
from pychromecast.controllers.plex import PlexController
from datetime import timedelta

import os
import json
import time

from .const import DOMAIN, _LOGGER
from .plex_assistant import PlexAssistant
from .process_speech import ProcessSpeech
from .localize import translations
from .helpers import (
cast_next_prev,
device_responding,
filter_media,
find_media,
fuzzy,
get_devices,
get_server,
jump,
listeners,
media_error,
media_service,
no_device_error,
play_tts_error,
process_config_item,
remote_control,
run_start_script,
seek_to_offset,
)


Expand All @@ -62,31 +60,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
tts_errors = entry.data.get("tts_errors")
lang = entry.data.get("language")
localize = translations[lang]
start_script = entry.options.get("start_script")
keyword_replace = entry.options.get("keyword_replace")
start_script = process_config_item(entry.options, "start_script")
keyword_replace = process_config_item(entry.options, "keyword_replace")
jump_amount = [entry.options.get("jump_f") or 30, entry.options.get("jump_b") or 15]
zeroconf = await async_get_instance(hass)
plex_c = PlexController()

if start_script:
try:
start_script = json.loads("{" + start_script + "}")
if start_script:
for script in start_script.keys():
_LOGGER.debug(f"Script {script}: {start_script[script]}")
except:
start_script = None
_LOGGER.warning("Plex Assistant: There is a formatting issue with your client start script config.")
start_script_keys = list(start_script.keys()) if start_script else []

if keyword_replace:
try:
keyword_replace = json.loads("{" + keyword_replace + "}")
for word in keyword_replace.keys():
_LOGGER.debug(f"Replace '{word}' with '{keyword_replace[word]}'")
except:
keyword_replace = None
_LOGGER.warning("Plex Assistant: There is a formatting issue with your keyword replacement config.")

server = await get_server(hass, hass.config, server_name)
if not server:
Expand All @@ -97,7 +74,7 @@ def pa_executor(_server, start_script_keys):
get_devices(hass, _pa)
return _pa

pa = await hass.async_add_executor_job(pa_executor, server, start_script_keys)
pa = await hass.async_add_executor_job(pa_executor, server, list(start_script.keys()))

ifttt_listener = await listeners(hass)
hass.data[DOMAIN][entry.entry_id] = {"remove_listener": ifttt_listener}
Expand All @@ -109,30 +86,23 @@ def pa_executor(_server, start_script_keys):
entry.add_update_listener(async_reload_entry)

def handle_input(call):
offset = None
media = None

hass.services.async_call("plex", "scan_for_clients", blocking=False, limit=30)
command = call.data.get("command").strip()
_LOGGER.debug("Command: %s", command)
media = None

if not command:
_LOGGER.warning(localize["no_call"])
return
_LOGGER.debug("Command: %s", command)

command = command.lower()

if keyword_replace and any(keyword.lower() in command for keyword in keyword_replace.keys()):
for keyword in keyword_replace.keys():
command = command.replace(keyword.lower(), keyword_replace[keyword].lower())

get_devices(hass, pa)

command = ProcessSpeech(pa, localize, command, default_device)
command = command.results

command_debug = {i: command[i] for i in command if i != "library" and command[i]}
command_debug = str(command_debug).replace("'", "").replace(":", " =")
_LOGGER.debug(f"Processed Command: {command_debug[1:-1]}")
command = ProcessSpeech(pa, localize, command, default_device).results
_LOGGER.debug("Processed Command: %s", {i: command[i] for i in command if i != "library" and command[i]})

if not command["device"] and not default_device:
no_device_error(localize)
Expand All @@ -142,118 +112,47 @@ def handle_input(call):
pa.update_libraries()

device = fuzzy(command["device"] or default_device, pa.device_names)

responding = True
if device[0] in start_script_keys:
timeout = 0
started = False
responding = False
woken = False
start_time = time.time()
while timeout < 30 and device[0] not in pa.devices:
started = True
if timeout == 0:
hass.services.call("script", start_script[device[0]].replace("script.", ""))
time.sleep(5)
hass.services.call("plex", "scan_for_clients")
else:
time.sleep(1)
if (timeout % 2) == 0 or timeout == 0:
get_devices(hass, pa)
timeout += 1

if started:
hass.services.async_call("plex", "scan_for_clients")
get_devices(hass, pa)

if device[0] in pa.devices:
stop = False
while not responding and not stop:
if not started:
hass.services.call("script", start_script[device[0]].replace("script.", ""))
responding = device_responding(hass, pa, device[0])
stop = True
total_time = timedelta(seconds=time.time()) - timedelta(seconds=start_time)

if responding and not started and total_time > timedelta(seconds=1):
time.sleep(5)

device = fuzzy(command["device"] or default_device, list(pa.devices.keys()))
device = run_start_script(hass, pa, command, start_script, device)

_LOGGER.debug("PA Devices: %s", pa.devices)

if device[1] < 60 or not responding:
if device[1] < 60:
no_device_error(localize, command["device"])
return
else:
_LOGGER.debug("Device: %s", device[0])
device = pa.devices[device[0]]
_LOGGER.debug("Device: %s", device[0])

if command["control"] == "jump_forward":
jump(hass, device, jump_amount[0])
return
elif command["control"] == "jump_back":
jump(hass, device, -jump_amount[1])
return
elif command["control"] == "next_track" and device["device_type"] == "cast":
cast_next_prev(hass, zeroconf, plex_c, device, "next")
return
elif command["control"] == "previous_track" and device["device_type"] == "cast":
cast_next_prev(hass, zeroconf, plex_c, device, "previous")
return
elif command["control"]:
media_service(hass, device["entity_id"], f"media_{command['control']}")
device = pa.devices[device[0]]

if command["control"]:
remote_control(hass, zeroconf, command["control"], device)
return

try:
result = find_media(command, command["media"], pa.media)
media = filter_media(pa, command, result["media"], result["library"])
media, library = find_media(command, command["media"], pa.media)
media = filter_media(pa, command, media, library)
except:
error = media_error(command, localize)
if tts_errors:
play_tts_error(hass, tts_dir, device["entity_id"], error, lang)

_LOGGER.debug("Media: %s", str(media))

if getattr(media, "viewOffset", 0) > 10 and not command["random"]:
offset = (media.viewOffset / 1000) - 5

shuffle = 1 if command["random"] else 0
offset = (media.viewOffset / 1000) - 5 if getattr(media, "viewOffset", 0) > 15 and not command["random"] else 0

if getattr(media, "TYPE", None) == "episode":
episodes = media.show().episodes()
episodes = episodes[episodes.index(media):]
media = pa.server.createPlayQueue(episodes, shuffle=shuffle)

if not getattr(media, "TYPE", None) == "playqueue":
elif not getattr(media, "TYPE", None) == "playqueue":
media = pa.server.createPlayQueue(media, shuffle=shuffle)

payload = '{"playqueue_id": %s, "type": "%s"}' % (
payload = '%s{"playqueue_id": %s, "type": "%s"}' % (
"plex://" if device["device_type"] in ["cast", "sonos"] else "",
media.playQueueID,
media.playQueueType,
media.playQueueType
)

if device["device_type"] == "cast":
payload = "plex://" + payload

media_service(hass, device["entity_id"], "play_media", payload)

if offset:
timeout = 0
while not hass.states.is_state(device["entity_id"], "playing") and timeout < 200:
time.sleep(0.25)
timeout += 1

if device["device_type"] == "cast":
timeout = 0
while hass.states.get(device["entity_id"]).attributes.get("media_position", 0) == 0 and timeout < 200:
time.sleep(0.25)
timeout += 1
else:
time.sleep(0.75)

if hass.states.is_state(device["entity_id"], "playing"):
media_service(hass, device["entity_id"], "media_seek", offset)
seek_to_offset(hass, offset, device["entity_id"])

hass.services.async_register(DOMAIN, "command", handle_input)
return True
Expand Down
Loading

0 comments on commit 984fdb7

Please sign in to comment.