Dashcast add-on
This commit is contained in:
Executable
+26
@@ -0,0 +1,26 @@
|
||||
# This Dockerfile will only work with host networking enabled.
|
||||
|
||||
# Use an official Python runtime as a parent image
|
||||
FROM python:3
|
||||
|
||||
# Set the working directory to /app
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the current directory contents into the container at /app
|
||||
ADD . /app
|
||||
|
||||
# Install any needed packages specified in requirements.txt
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Define environment variables
|
||||
ENV DEFAULT_DASHBOARD_URL "https://darksky.net"
|
||||
ENV DEFAULT_DASHBOARD_URL_FORCE "False"
|
||||
ENV DISPLAY_NAME ""
|
||||
ENV IGNORE_CEC "True"
|
||||
|
||||
ENV MQTT_SERVER "iot.eclipse.org"
|
||||
ENV MQTT_USERNAME ""
|
||||
ENV MQTT_PASSWORD ""
|
||||
|
||||
# Run app.sh when the container launches
|
||||
CMD ["./app.sh"]
|
||||
@@ -5,6 +5,8 @@
|
||||
This is a Dashcast add-on for Home Assistant.
|
||||
Dascast is an app that displays dashboard web pages on your Chromecast device.
|
||||
|
||||
Credit to [ragingcomputer/dashcast-docker-MQTT](https://github.com/ragingcomputer/dashcast-docker-MQTT) for the docker code.
|
||||
|
||||
## Installation
|
||||
|
||||
The installation of this add-on is pretty straightforward and not different in
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Keep the python app alive.
|
||||
while true; do python -u app_mqtt.py --show-debug; done
|
||||
Executable
+225
@@ -0,0 +1,225 @@
|
||||
"""
|
||||
Run DashCast semi-persistently on a Chromecast while allowing other
|
||||
Chromecast apps to work also by only launching when idle.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import json
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
import pychromecast
|
||||
import pychromecast.controllers.dashcast as dashcast
|
||||
|
||||
|
||||
print('DashCast')
|
||||
print('Searching for Chromecasts...')
|
||||
|
||||
DEFAULT_DASHBOARD_URL = os.getenv('DEFAULT_DASHBOARD_URL', 'https://home-assistant.io')
|
||||
DEFAULT_DASHBOARD_URL_FORCE = os.getenv('DEFAULT_DASHBOARD_URL_FORCE') == 'True'
|
||||
DISPLAY_NAME = os.getenv('DISPLAY_NAME')
|
||||
IGNORE_CEC = os.getenv('IGNORE_CEC') == 'True'
|
||||
|
||||
SUBSCRIBE = 'chromecast/' + str(DISPLAY_NAME) + '/command/dashcast'
|
||||
MQTT_SERVER = os.getenv('MQTT_SERVER', 'iot.eclipse.org')
|
||||
MQTT_USERNAME = os.getenv('MQTT_USERNAME')
|
||||
MQTT_PASSWORD = os.getenv('MQTT_PASSWORD')
|
||||
|
||||
if IGNORE_CEC:
|
||||
print('Ignoring CEC for Chromecast', DISPLAY_NAME)
|
||||
pychromecast.IGNORE_CEC.append(DISPLAY_NAME)
|
||||
|
||||
|
||||
if '--show-debug' in sys.argv:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
class DashboardLauncher():
|
||||
|
||||
def __init__(self, device, dashboard_url='https://home-assistant.io',
|
||||
dashboard_url_force=False, dashboard_app_name='DashCast'):
|
||||
self.device = device
|
||||
print('DashboardLauncher', self.device.name)
|
||||
|
||||
# register dashcast controller
|
||||
self.controller = dashcast.DashCastController()
|
||||
self.device.register_handler(self.controller)
|
||||
|
||||
# Breaking Change pychromecast 3.0
|
||||
# Do not automatically start worker thread and connect in Chromecast constructor
|
||||
# https://github.com/balloob/pychromecast/pull/271
|
||||
self.device.wait()
|
||||
|
||||
# register status listener
|
||||
receiver_controller = device.socket_client.receiver_controller
|
||||
receiver_controller.register_status_listener(self)
|
||||
|
||||
# register connection listener
|
||||
self.device.socket_client.media_controller.register_status_listener(self)
|
||||
self.device.register_connection_listener(self)
|
||||
|
||||
# set default url and dashboard app name
|
||||
self.dashboard_url = dashboard_url
|
||||
self.dashboard_url_force = dashboard_url_force
|
||||
self.dashboard_app_name = dashboard_app_name
|
||||
self.takeover = False
|
||||
|
||||
# Check status on init.
|
||||
self.new_cast_status(self.device.status)
|
||||
# Launch dashboard on init.
|
||||
self.launch_dashboard(self.dashboard_url, self.dashboard_url_force)
|
||||
|
||||
# While dashboard is launching, start MQTT connection
|
||||
print("Starting MQTT")
|
||||
client = mqtt.Client("client_" + str(DISPLAY_NAME)) # create new instance
|
||||
client.on_message = self.on_message # attach function to callback
|
||||
client.on_connect = self.on_connect
|
||||
print("Connecting to Broker: " + MQTT_SERVER)
|
||||
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
|
||||
client.connect(MQTT_SERVER) # connect to broker
|
||||
|
||||
# wait for dashboard to finish loading, then start MQTT client loop
|
||||
time.sleep(10)
|
||||
client.loop_start()
|
||||
|
||||
# keep_looping is the control variable.
|
||||
# If it is False, loop stops, program cleans up and exits
|
||||
self.keep_looping = True
|
||||
|
||||
# if launch_it is set to true, the dashboard is relaunched
|
||||
# otherwise sleep a second and check again
|
||||
self.launch_it = False
|
||||
while self.keep_looping:
|
||||
if self.launch_it:
|
||||
print('launch_it', self.launch_it)
|
||||
self.launch_dashboard(self.dashboard_url, self.dashboard_url_force)
|
||||
self.launch_it = False
|
||||
time.sleep(10)
|
||||
time.sleep(1)
|
||||
|
||||
client.loop_stop()
|
||||
|
||||
# define MQTT client callbacks
|
||||
|
||||
def on_connect(self, client, userdata, flags, rc):
|
||||
print("Connected with result code "+str(rc))
|
||||
# Subscribing in on_connect() means that if we lose the connection and
|
||||
# reconnect then subscriptions will be renewed.
|
||||
print("Subscribing to topic ", SUBSCRIBE)
|
||||
client.subscribe(SUBSCRIBE)
|
||||
|
||||
def on_message(self, client, userdata, message):
|
||||
print("Message received: ", str(message.payload.decode("utf-8")))
|
||||
print("-Topic: ", message.topic)
|
||||
print("-QOS: ", message.qos)
|
||||
print("-Retain: ", message.retain)
|
||||
|
||||
# try decoding received message
|
||||
try:
|
||||
json_decode = str(message.payload.decode("utf-8", "ignore"))
|
||||
print("Decoding Json")
|
||||
parsed_json = json.loads(json_decode)
|
||||
except json.decoder.JSONDecodeError:
|
||||
print("Error passing JSON")
|
||||
return
|
||||
|
||||
# set dashboard_url to received data and
|
||||
# set to relaunch/update dashboard
|
||||
print("Chromecast: " + DISPLAY_NAME)
|
||||
if 'url' in parsed_json:
|
||||
print("Url: " + parsed_json["url"])
|
||||
self.dashboard_url = parsed_json["url"]
|
||||
|
||||
if 'force' in parsed_json:
|
||||
print("Force: " + str(parsed_json["force"]))
|
||||
self.dashboard_url_force = parsed_json["force"]
|
||||
else:
|
||||
self.dashboard_url_force = False
|
||||
|
||||
if 'takeover' in parsed_json:
|
||||
print("Takeover: " + str(parsed_json["takeover"]))
|
||||
self.takeover = parsed_json["takeover"]
|
||||
else:
|
||||
self.takeover = False
|
||||
|
||||
self.dashboard_url = parsed_json["url"]
|
||||
|
||||
if self.is_dashboard_active() or self.takeover:
|
||||
self.launch_it = True
|
||||
|
||||
# Define pychromecast Callback Receivers
|
||||
def new_cast_status(self, cast_status):
|
||||
""" Called when a new cast status has been received. """
|
||||
print('new_cast_status', self.device.name, cast_status)
|
||||
|
||||
def should_launch():
|
||||
""" If the device is active, the dashboard is not already active, and no other app is active. """
|
||||
print('should launch', self.is_device_active(),
|
||||
not self.is_dashboard_active(), not self.is_other_app_active())
|
||||
return (self.is_device_active()
|
||||
and not self.is_dashboard_active()
|
||||
and not self.is_other_app_active())
|
||||
|
||||
if should_launch():
|
||||
self.launch_it = True
|
||||
|
||||
def new_media_status(self, media_status):
|
||||
"""Called when a new MediaStatus is received."""
|
||||
print('new_media_status', self.device.name, media_status)
|
||||
|
||||
def new_connection_status(self, connection_status):
|
||||
"""Called when a new ConnectionStatus is received."""
|
||||
print('new_connection_status', self.device.name, connection_status)
|
||||
|
||||
def is_device_active(self):
|
||||
""" Returns if there is currently an app running and (maybe) visible. """
|
||||
|
||||
return (self.device.status is not None
|
||||
#and self.device.app_id is not None
|
||||
and (self.device.status.is_active_input or self.device.ignore_cec)
|
||||
#and (not self.device.status.is_stand_by and not self.device.ignore_cec)
|
||||
)
|
||||
|
||||
def is_dashboard_active(self):
|
||||
""" Returns if the dashboard is (probably) visible. """
|
||||
return self.device.app_display_name == self.dashboard_app_name
|
||||
|
||||
def is_other_app_active(self):
|
||||
""" Returns if an app other than the dashboard or the Backdrop is (probably) visible. """
|
||||
return self.device.app_display_name not in ('Backdrop', self.dashboard_app_name)
|
||||
|
||||
def launch_dashboard(self, url_to_load, force):
|
||||
print('Launching dashboard on Chromecast', self.device.name)
|
||||
|
||||
def callback(response):
|
||||
print('callback called', response)
|
||||
|
||||
try:
|
||||
self.controller.load_url(url_to_load, force, callback_function=callback)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
|
||||
|
||||
casts = pychromecast.get_chromecasts()
|
||||
if len(casts) == 0:
|
||||
print('No Devices Found')
|
||||
exit()
|
||||
|
||||
for cc in casts:
|
||||
print('enumerate-casts', cc.device.friendly_name)
|
||||
|
||||
cast = next(cc for cc in casts if DISPLAY_NAME in (None, '') or cc.device.friendly_name == DISPLAY_NAME)
|
||||
|
||||
if not cast:
|
||||
print('Chromecast with name', DISPLAY_NAME, 'not found')
|
||||
exit()
|
||||
|
||||
DashboardLauncher(cast, dashboard_url=DEFAULT_DASHBOARD_URL, dashboard_url_force=DEFAULT_DASHBOARD_URL_FORCE)
|
||||
|
||||
# Keep running
|
||||
while True:
|
||||
time.sleep(1)
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Dashcast",
|
||||
"version": "latest",
|
||||
"version": "0.0.1",
|
||||
"slug": "dashcast",
|
||||
"description": "Display dashboard web pages on your Chromecast device",
|
||||
"url": "https://github.com/ragingcomputer/dashcast-docker-MQTT",
|
||||
@@ -23,7 +23,6 @@
|
||||
"MQTT_USERNAME": "mqtt",
|
||||
"MQTT_PASSWORD": "testpassword"
|
||||
},
|
||||
"image": "ragingcomputer/dashcast-docker-mqtt",
|
||||
"options": {
|
||||
"log_level": "info",
|
||||
"chromecast_name": "Livingroom Chromecast",
|
||||
|
||||
Reference in New Issue
Block a user