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.
|
This is a Dashcast add-on for Home Assistant.
|
||||||
Dascast is an app that displays dashboard web pages on your Chromecast device.
|
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
|
## Installation
|
||||||
|
|
||||||
The installation of this add-on is pretty straightforward and not different in
|
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",
|
"name": "Dashcast",
|
||||||
"version": "latest",
|
"version": "0.0.1",
|
||||||
"slug": "dashcast",
|
"slug": "dashcast",
|
||||||
"description": "Display dashboard web pages on your Chromecast device",
|
"description": "Display dashboard web pages on your Chromecast device",
|
||||||
"url": "https://github.com/ragingcomputer/dashcast-docker-MQTT",
|
"url": "https://github.com/ragingcomputer/dashcast-docker-MQTT",
|
||||||
@@ -23,7 +23,6 @@
|
|||||||
"MQTT_USERNAME": "mqtt",
|
"MQTT_USERNAME": "mqtt",
|
||||||
"MQTT_PASSWORD": "testpassword"
|
"MQTT_PASSWORD": "testpassword"
|
||||||
},
|
},
|
||||||
"image": "ragingcomputer/dashcast-docker-mqtt",
|
|
||||||
"options": {
|
"options": {
|
||||||
"log_level": "info",
|
"log_level": "info",
|
||||||
"chromecast_name": "Livingroom Chromecast",
|
"chromecast_name": "Livingroom Chromecast",
|
||||||
|
|||||||
Reference in New Issue
Block a user