From fdc509f402879af71fa88af1a0f383efde3efdf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Tue, 17 Sep 2019 23:12:29 +0200 Subject: [PATCH] Huge restructure in progress --- custom_components/browser_mod/__init__.py | 9 +- custom_components/browser_mod/browser_mod.js | 2 +- custom_components/browser_mod/connection.py | 136 ++++++++++-------- custom_components/browser_mod/helpers.py | 58 ++++++++ custom_components/browser_mod/light.py | 52 +++++++ custom_components/browser_mod/media_player.py | 54 +++---- custom_components/browser_mod/sensor.py | 36 +++++ js/main.js | 15 +- 8 files changed, 264 insertions(+), 98 deletions(-) create mode 100644 custom_components/browser_mod/helpers.py create mode 100644 custom_components/browser_mod/light.py create mode 100644 custom_components/browser_mod/sensor.py diff --git a/custom_components/browser_mod/__init__.py b/custom_components/browser_mod/__init__.py index 25e1e48..1f88d95 100644 --- a/custom_components/browser_mod/__init__.py +++ b/custom_components/browser_mod/__init__.py @@ -21,13 +21,18 @@ async def async_setup(hass, config): hass.data[DOMAIN] = { DATA_DEVICES: {}, DATA_ALIASES: aliases, - DATA_ADDERS: [], + DATA_ADDERS: {}, } await hass.helpers.discovery.async_load_platform("media_player", DOMAIN, {}, config) + await hass.helpers.discovery.async_load_platform("sensor", DOMAIN, {}, config) + await hass.helpers.discovery.async_load_platform("light", DOMAIN, {}, config) - setup_connection(hass) + await setup_connection(hass, config) setup_service(hass) return True + + + diff --git a/custom_components/browser_mod/browser_mod.js b/custom_components/browser_mod/browser_mod.js index 0b0c678..fd29071 100644 --- a/custom_components/browser_mod/browser_mod.js +++ b/custom_components/browser_mod/browser_mod.js @@ -202,7 +202,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _car /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! /card-tools/deviceId */ \"../../../card-tools/deviceId.js\");\n/* harmony import */ var _card_tools_hass__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! /card-tools/hass */ \"../../../card-tools/hass.js\");\n/* harmony import */ var _card_tools_popup__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! /card-tools/popup */ \"../../../card-tools/popup.js\");\n/* harmony import */ var _card_tools_event__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! /card-tools/event */ \"../../../card-tools/event.js\");\n/* harmony import */ var _card_tools_more_info_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! /card-tools/more-info.js */ \"../../../card-tools/more-info.js\");\n/* harmony import */ var _browser_player__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./browser-player */ \"./js/browser-player.js\");\n\n\n\n\n\n\n\nclass BrowserMod {\n\n set hass(hass) {\n if(!hass) return;\n this._hass = hass;\n if(this.hassPatched) return;\n const callService = hass.callService;\n const newCallService = (domain, service, serviceData) => {\n if(domain === \"browser_mod\" && service === \"command\") {\n if(serviceData.deviceID) {\n const index = serviceData.deviceID.indexOf('this');\n if(index !== -1)\n serviceData.deviceID[index] = _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"];\n }\n }\n return callService(domain, service, serviceData);\n };\n hass.callService = newCallService;\n\n this.hassPatched = true;\n document.querySelector(\"home-assistant\").hassChanged(hass, hass);\n }\n\n playOnce(ev) {\n if(window.browser_mod.playedOnce) return;\n window.browser_mod.player.play();\n window.browser_mod.playedOnce = true;\n }\n\n constructor() {\n window.hassConnection.then((conn) => this.connect(conn.conn));\n this.player = new Audio();\n this.playedOnce = false;\n\n this.autoclose_popup_active = false;\n\n const updater = this.update.bind(this);\n this.player.addEventListener(\"ended\", updater);\n this.player.addEventListener(\"play\", updater);\n this.player.addEventListener(\"pause\", updater);\n this.player.addEventListener(\"volumechange\", updater);\n document.addEventListener(\"visibilitychange\", updater);\n window.addEventListener(\"location-changed\", updater);\n window.addEventListener(\"click\", this.playOnce);\n window.addEventListener(\"mousemove\", this.no_blackout.bind(this));\n window.addEventListener(\"mousedown\", this.no_blackout.bind(this));\n window.addEventListener(\"keydown\", this.no_blackout.bind(this));\n window.addEventListener(\"touchstart\", this.no_blackout.bind(this));\n Object(_card_tools_hass__WEBPACK_IMPORTED_MODULE_1__[\"provideHass\"])(this);\n\n if(window.fully)\n {\n this._fullyMotion = false;\n this._motionTimeout = undefined;\n fully.bind('screenOn', 'browser_mod.update();');\n fully.bind('screenOff', 'browser_mod.update();');\n fully.bind('onMotion', 'browser_mod.fullyMotion();');\n }\n\n this._blackout = document.createElement(\"div\");\n this._blackout.style.cssText = `\n position: fixed;\n left: 0;\n top: 0;\n padding: 0;\n margin: 0;\n width: 100%;\n height: 100%;\n background: black;\n visibility: hidden;\n `;\n document.body.appendChild(this._blackout);\n }\n\n connect(conn) {\n this.conn = conn\n conn.subscribeMessage((msg) => this.callback(msg), {\n type: 'browser_mod/connect',\n deviceID: _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"],\n });\n }\n\n callback(msg) {\n switch (msg.command) {\n case \"update\":\n this.update(msg);\n break;\n\n case \"debug\":\n this.debug(msg);\n break;\n\n case \"play\":\n this.play(msg);\n break;\n case \"pause\":\n this.pause(msg);\n break;\n case \"stop\":\n this.stop(msg);\n break;\n case \"set_volume\":\n this.set_volume(msg);\n break;\n case \"mute\":\n this.mute(msg);\n break;\n\n case \"popup\":\n this.popup(msg);\n break;\n case \"close-popup\":\n this.close_popup(msg);\n break;\n case \"navigate\":\n this.navigate(msg);\n break;\n case \"more-info\":\n this.more_info(msg);\n break;\n case \"set-theme\":\n this.set_theme(msg);\n break;\n\n case \"lovelace-reload\":\n this.lovelace_reload(msg);\n break;\n\n case \"blackout\":\n this.blackout(msg);\n break;\n case \"no-blackout\":\n this.no_blackout(msg);\n break;\n }\n }\n\n get player_state() {\n if (!this.player.src) return \"stopped\";\n if (this.player.ended) return \"stopped\";\n if (this.player.paused) return \"paused\";\n return \"playing\";\n }\n\n debug(msg) {\n Object(_card_tools_popup__WEBPACK_IMPORTED_MODULE_2__[\"popUp\"])(`deviceID`, {type: \"markdown\", content: `# ${_card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"]}`})\n alert(_card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"]);\n }\n\n play(msg) {\n const src = msg.media_content_id;\n if(src)\n this.player.src = src;\n this.player.play();\n }\n pause(msg) {\n this.player.pause();\n }\n stop(msg) {\n this.player.pause();\n this.player.src = null;\n }\n set_volume(msg) {\n if (msg.volume_level === undefined) return;\n this.player.volume = msg.volume_level;\n }\n mute(msg) {\n if (msg.mute === undefined)\n msg.mute = !this.player.muted;\n this.player.muted = Boolean(msg.mute)\n }\n\n popup(msg){\n if(!msg.title && !msg.auto_close) return;\n if(!msg.card) return;\n Object(_card_tools_popup__WEBPACK_IMPORTED_MODULE_2__[\"popUp\"])(msg.title, msg.card, msg.large, msg.style, msg.auto_close);\n if(msg.auto_close)\n this.autoclose_popup_active = true;\n }\n close_popup(msg){\n this.autoclose_popup_active = false;\n Object(_card_tools_popup__WEBPACK_IMPORTED_MODULE_2__[\"closePopUp\"])();\n }\n navigate(msg){\n if(!msg.navigation_path) return;\n history.pushState(null, \"\", msg.navigation_path);\n Object(_card_tools_event__WEBPACK_IMPORTED_MODULE_3__[\"fireEvent\"])(\"location-changed\", {}, document.querySelector(\"home-assistant\"));\n }\n more_info(msg){\n if(!msg.entity_id) return;\n Object(_card_tools_more_info_js__WEBPACK_IMPORTED_MODULE_4__[\"moreInfo\"])(msg.entity_id, msg.large);\n }\n set_theme(msg){\n if(!msg.theme) msg.theme = \"default\";\n Object(_card_tools_event__WEBPACK_IMPORTED_MODULE_3__[\"fireEvent\"])(\"settheme\", msg.theme, document.querySelector(\"home-assistant\"));\n }\n\n lovelace_reload(msg) {\n const ll = Object(_card_tools_hass__WEBPACK_IMPORTED_MODULE_1__[\"lovelace_view\"])();\n if (ll)\n Object(_card_tools_event__WEBPACK_IMPORTED_MODULE_3__[\"fireEvent\"])(\"config-refresh\", {}, ll);\n }\n\n blackout(msg){\n if (window.fully)\n {\n fully.turnScreenOff();\n } else {\n this._blackout.style.visibility = \"visible\";\n }\n this.update();\n }\n no_blackout(msg){\n if(this.autoclose_popup_active)\n return this.close_popup();\n if (window.fully)\n {\n if (!fully.getScreenOn())\n fully.turnScreenOn();\n if (msg.brightness)\n fully.setScreenBrightness(msg.brightness);\n this.update();\n } else {\n if(this._blackout.style.visibility !== \"hidden\") {\n this._blackout.style.visibility = \"hidden\";\n this.update();\n }\n }\n }\n is_blackout(){\n if (window.fully)\n return !fully.getScreenOn();\n return Boolean(this._blackout.style.visibility === \"visible\")\n }\n\n fullyMotion() {\n this._fullyMotion = true;\n clearTimeout(this._motionTimeout);\n this._motionTimeout = setTimeout(() => {\n this._fullyMotion = false;\n this.update();\n }, 5000);\n this.update();\n }\n\n\n update(msg=null) {\n if(!this.conn) return;\n\n if(msg)\n if(msg.entity_id)\n this.entity_id = msg.entity_id;\n\n this.conn.sendMessage({\n type: 'browser_mod/update',\n deviceID: _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"],\n data: {\n browser: {\n path: window.location.pathname,\n visibility: document.visibilityState,\n userAgent: navigator.userAgent,\n currentUser: this._hass && this._hass.user && this._hass.user.name,\n blackout: this.is_blackout(),\n fullyKiosk: window.fully ? true : undefined,\n brightness: window.fully ? fully.getScreenBrightness() : undefined,\n battery: window.fully ? fully.getBatteryLevel() : undefined,\n charging: window.fully ? fully.isPlugged(): undefined,\n motion: window.fully ? this._fullyMotion : undefined,\n },\n player: {\n volume: this.player.volume,\n muted: this.player.muted,\n src: this.player.src,\n state: this.player_state,\n },\n },\n });\n\n }\n\n}\n\nwindow.browser_mod = new BrowserMod();\n\n\n\n//# sourceURL=webpack:///./js/main.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! /card-tools/deviceId */ \"../../../card-tools/deviceId.js\");\n/* harmony import */ var _card_tools_hass__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! /card-tools/hass */ \"../../../card-tools/hass.js\");\n/* harmony import */ var _card_tools_popup__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! /card-tools/popup */ \"../../../card-tools/popup.js\");\n/* harmony import */ var _card_tools_event__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! /card-tools/event */ \"../../../card-tools/event.js\");\n/* harmony import */ var _card_tools_more_info_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! /card-tools/more-info.js */ \"../../../card-tools/more-info.js\");\n/* harmony import */ var _browser_player__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./browser-player */ \"./js/browser-player.js\");\n\n\n\n\n\n\n\nclass BrowserMod {\n\n set hass(hass) {\n if(!hass) return;\n this._hass = hass;\n if(this.hassPatched) return;\n const callService = hass.callService;\n const newCallService = (domain, service, serviceData) => {\n if(domain === \"browser_mod\" && service === \"command\") {\n if(serviceData.deviceID) {\n const index = serviceData.deviceID.indexOf('this');\n if(index !== -1)\n serviceData.deviceID[index] = _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"];\n }\n }\n return callService(domain, service, serviceData);\n };\n hass.callService = newCallService;\n\n this.hassPatched = true;\n document.querySelector(\"home-assistant\").hassChanged(hass, hass);\n }\n\n playOnce(ev) {\n if(window.browser_mod.playedOnce) return;\n window.browser_mod.player.play();\n window.browser_mod.playedOnce = true;\n }\n\n constructor() {\n window.hassConnection.then((conn) => this.connect(conn.conn));\n this.player = new Audio();\n this.playedOnce = false;\n\n this.autoclose_popup_active = false;\n\n const updater = this.update.bind(this);\n this.player.addEventListener(\"ended\", updater);\n this.player.addEventListener(\"play\", updater);\n this.player.addEventListener(\"pause\", updater);\n this.player.addEventListener(\"volumechange\", updater);\n document.addEventListener(\"visibilitychange\", updater);\n window.addEventListener(\"location-changed\", updater);\n window.addEventListener(\"click\", this.playOnce);\n window.addEventListener(\"mousemove\", this.no_blackout.bind(this));\n window.addEventListener(\"mousedown\", this.no_blackout.bind(this));\n window.addEventListener(\"keydown\", this.no_blackout.bind(this));\n window.addEventListener(\"touchstart\", this.no_blackout.bind(this));\n Object(_card_tools_hass__WEBPACK_IMPORTED_MODULE_1__[\"provideHass\"])(this);\n\n if(window.fully)\n {\n this._fullyMotion = false;\n this._motionTimeout = undefined;\n fully.bind('screenOn', 'browser_mod.update();');\n fully.bind('screenOff', 'browser_mod.update();');\n fully.bind('onMotion', 'browser_mod.fullyMotion();');\n }\n\n this._blackout = document.createElement(\"div\");\n this._blackout.style.cssText = `\n position: fixed;\n left: 0;\n top: 0;\n padding: 0;\n margin: 0;\n width: 100%;\n height: 100%;\n background: black;\n visibility: hidden;\n `;\n document.body.appendChild(this._blackout);\n }\n\n connect(conn) {\n this.conn = conn\n conn.subscribeMessage((msg) => this.callback(msg), {\n type: 'browser_mod/connect',\n deviceID: _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"],\n fully: window.fully ? true : undefined,\n });\n }\n\n callback(msg) {\n switch (msg.command) {\n case \"update\":\n this.update(msg);\n break;\n\n case \"debug\":\n this.debug(msg);\n break;\n\n case \"play\":\n this.play(msg);\n break;\n case \"pause\":\n this.pause(msg);\n break;\n case \"stop\":\n this.stop(msg);\n break;\n case \"set_volume\":\n this.set_volume(msg);\n break;\n case \"mute\":\n this.mute(msg);\n break;\n\n case \"popup\":\n this.popup(msg);\n break;\n case \"close-popup\":\n this.close_popup(msg);\n break;\n case \"navigate\":\n this.navigate(msg);\n break;\n case \"more-info\":\n this.more_info(msg);\n break;\n case \"set-theme\":\n this.set_theme(msg);\n break;\n\n case \"lovelace-reload\":\n this.lovelace_reload(msg);\n break;\n\n case \"blackout\":\n this.blackout(msg);\n break;\n case \"no-blackout\":\n this.no_blackout(msg);\n break;\n }\n }\n\n get player_state() {\n if (!this.player.src) return \"stopped\";\n if (this.player.ended) return \"stopped\";\n if (this.player.paused) return \"paused\";\n return \"playing\";\n }\n\n debug(msg) {\n Object(_card_tools_popup__WEBPACK_IMPORTED_MODULE_2__[\"popUp\"])(`deviceID`, {type: \"markdown\", content: `# ${_card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"]}`})\n alert(_card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"]);\n }\n\n play(msg) {\n const src = msg.media_content_id;\n if(src)\n this.player.src = src;\n this.player.play();\n }\n pause(msg) {\n this.player.pause();\n }\n stop(msg) {\n this.player.pause();\n this.player.src = null;\n }\n set_volume(msg) {\n if (msg.volume_level === undefined) return;\n this.player.volume = msg.volume_level;\n }\n mute(msg) {\n if (msg.mute === undefined)\n msg.mute = !this.player.muted;\n this.player.muted = Boolean(msg.mute)\n }\n\n popup(msg){\n if(!msg.title && !msg.auto_close) return;\n if(!msg.card) return;\n Object(_card_tools_popup__WEBPACK_IMPORTED_MODULE_2__[\"popUp\"])(msg.title, msg.card, msg.large, msg.style, msg.auto_close);\n if(msg.auto_close)\n this.autoclose_popup_active = true;\n }\n close_popup(msg){\n this.autoclose_popup_active = false;\n Object(_card_tools_popup__WEBPACK_IMPORTED_MODULE_2__[\"closePopUp\"])();\n }\n navigate(msg){\n if(!msg.navigation_path) return;\n history.pushState(null, \"\", msg.navigation_path);\n Object(_card_tools_event__WEBPACK_IMPORTED_MODULE_3__[\"fireEvent\"])(\"location-changed\", {}, document.querySelector(\"home-assistant\"));\n }\n more_info(msg){\n if(!msg.entity_id) return;\n Object(_card_tools_more_info_js__WEBPACK_IMPORTED_MODULE_4__[\"moreInfo\"])(msg.entity_id, msg.large);\n }\n set_theme(msg){\n if(!msg.theme) msg.theme = \"default\";\n Object(_card_tools_event__WEBPACK_IMPORTED_MODULE_3__[\"fireEvent\"])(\"settheme\", msg.theme, document.querySelector(\"home-assistant\"));\n }\n\n lovelace_reload(msg) {\n const ll = Object(_card_tools_hass__WEBPACK_IMPORTED_MODULE_1__[\"lovelace_view\"])();\n if (ll)\n Object(_card_tools_event__WEBPACK_IMPORTED_MODULE_3__[\"fireEvent\"])(\"config-refresh\", {}, ll);\n }\n\n blackout(msg){\n if (window.fully)\n {\n fully.turnScreenOff();\n } else {\n this._blackout.style.visibility = \"visible\";\n }\n this.update();\n }\n no_blackout(msg){\n if(this.autoclose_popup_active)\n return this.close_popup();\n if (window.fully)\n {\n if (!fully.getScreenOn())\n fully.turnScreenOn();\n if (msg.brightness)\n fully.setScreenBrightness(msg.brightness);\n this.update();\n } else {\n if(this._blackout.style.visibility !== \"hidden\") {\n this._blackout.style.visibility = \"hidden\";\n this.update();\n }\n }\n }\n is_blackout(){\n if (window.fully)\n return !fully.getScreenOn();\n return Boolean(this._blackout.style.visibility === \"visible\")\n }\n\n fullyMotion() {\n this._fullyMotion = true;\n clearTimeout(this._motionTimeout);\n this._motionTimeout = setTimeout(() => {\n this._fullyMotion = false;\n this.update();\n }, 5000);\n this.update();\n }\n\n\n update(msg=null) {\n if(!this.conn) return;\n\n if(msg)\n if(msg.entity_id)\n this.entity_id = msg.entity_id;\n\n this.conn.sendMessage({\n type: 'browser_mod/update',\n deviceID: _card_tools_deviceId__WEBPACK_IMPORTED_MODULE_0__[\"deviceID\"],\n data: {\n browser: {\n path: window.location.pathname,\n visibility: document.visibilityState,\n userAgent: navigator.userAgent,\n currentUser: this._hass && this._hass.user && this._hass.user.name,\n fullyKiosk: window.fully ? true : undefined,\n },\n player: {\n volume: this.player.volume,\n muted: this.player.muted,\n src: this.player.src,\n state: this.player_state,\n },\n screen: {\n blackout: this.is_blackout(),\n brightness: window.fully ? fully.getScreenBrightness() : undefined,\n },\n fully: window.fully ? {\n battery: window.fully ? fully.getBatteryLevel() : undefined,\n charging: window.fully ? fully.isPlugged(): undefined,\n motion: window.fully ? this._fullyMotion : undefined,\n } : undefined,\n },\n });\n\n }\n\n}\n\nwindow.browser_mod = new BrowserMod();\n\n\n\n//# sourceURL=webpack:///./js/main.js?"); /***/ }) diff --git a/custom_components/browser_mod/connection.py b/custom_components/browser_mod/connection.py index d3324ec..0f29a01 100644 --- a/custom_components/browser_mod/connection.py +++ b/custom_components/browser_mod/connection.py @@ -4,78 +4,100 @@ import voluptuous as vol from homeassistant.components.websocket_api import websocket_command, result_message, event_message, async_register_command from homeassistant.helpers.entity import Entity, async_generate_entity_id -from .const import DOMAIN, DATA_DEVICES, DATA_ADDERS, WS_CONNECT, WS_UPDATE +from .const import DOMAIN, WS_CONNECT, WS_UPDATE +from .helpers import get_devices, create_entity _LOGGER = logging.getLogger(__name__) +async def setup_connection(hass, config): + _LOGGER.error("--------------------") + _LOGGER.error("Setting up BM connection") + + @websocket_command({ + vol.Required("type"): WS_CONNECT, + vol.Required("deviceID"): str, + }) + def handle_connect(hass, connection, msg): + _LOGGER.error("--------------------") + _LOGGER.error("CONNECTING BM") + deviceID = msg["deviceID"] + + device = get_devices(hass).get(deviceID, BrowserModConnection(hass, deviceID)) + device.connect(connection, msg["id"]) + get_devices(hass)[deviceID] = device + + _LOGGER.error("DONE") + connection.send_message(result_message(msg["id"])) + + @websocket_command({ + vol.Required("type"): WS_UPDATE, + vol.Required("deviceID"): str, + vol.Optional("data"): dict, + }) + def handle_update( hass, connection, msg): + _LOGGER.error("--------------------") + _LOGGER.error("UPDATING BM") + _LOGGER.error(msg) + devices = get_devices(hass) + deviceID = msg["deviceID"] + if deviceID in devices: + devices[deviceID].update(msg.get("data", None)) -def setup_connection(hass): async_register_command(hass, handle_connect) async_register_command(hass, handle_update) -@websocket_command({ - vol.Required("type"): WS_CONNECT, - vol.Required("deviceID"): str, -}) -def handle_connect(hass, connection, msg): +class BrowserModConnection: + def __init__(self, hass, deviceID): + self.hass = hass + self.deviceID = deviceID + self.connection = [] - devices = hass.data[DOMAIN][DATA_DEVICES] - deviceID = msg["deviceID"] - if deviceID in devices: - devices[deviceID].ws_connect(connection, msg["id"]) - else: - adder = hass.data[DOMAIN][DATA_ADDERS][0] - devices[deviceID] = adder(hass, deviceID, connection, msg["id"]) - connection.send_message(result_message(msg["id"])) + self.media_player = None + self.screen = None + self.sensor = None + def connect(self, connection, cid): + self.connection.append((connection, cid)) + _LOGGER.error("********************") + _LOGGER.error("Connected %s", self.deviceID) + self.send("update") -@websocket_command({ - vol.Required("type"): WS_UPDATE, - vol.Required("deviceID"): str, - vol.Optional("data"): dict, -}) -def handle_update(hass, connection, msg): - devices = hass.data[DOMAIN][DATA_DEVICES] - deviceID = msg["deviceID"] - if deviceID in devices: - devices[deviceID].ws_update(msg.get("data", None)) + def disconnect(): + self.connection.remove((connection, cid)) + connection.subscriptions[cid] = disconnect -class BrowserModEntity(Entity): - def __init__(self, hass, deviceID, alias=None): - self._deviceID = deviceID - self._alias = alias - self._ws_data = {} - self._ws_connection = None - self.entity_id = async_generate_entity_id("media_player.{}", alias or deviceID, hass=hass) - - def ws_send(self, command, **kwargs): - if self._ws_connection: - self._ws_connection.send_message(event_message(self._ws_cid, { + def send(self, command, **kwargs): + if self.connection: + connection, cid = self.connection[-1] + connection.send_message(event_message(cid, { "command": command, **kwargs, })) - def ws_connect(self, connection, cid): - self._ws_cid = cid - self._ws_connection = connection - self.ws_send("update", entity_id=self.entity_id) - connection.subscriptions[cid] = self.ws_disconnect - if self.hass: - self.schedule_update_ha_state() + def update(self, data): + _LOGGER.error("********************") + _LOGGER.error("Got update %s for %s", data, self.deviceID) + if data.get('player'): + self.media_player = self.media_player or create_entity( + self.hass, + 'media_player', + self.deviceID, + self) + self.media_player.data = data.get('player') + if data.get('browser'): + self.sensor = self.sensor or create_entity( + self.hass, + 'sensor', + self.deviceID, + self) + self.sensor.data = data.get('browser') + if data.get('screen'): + self.screen = self.screen or create_entity( + self.hass, + 'light', + self.deviceID, + self) + self.screen.data = data.get('screen') - def ws_disconnect(self): - self._ws_cid = None - self._ws_connection = None - if self.hass: - self.schedule_update_ha_state() - - def ws_update(self, data): - self._ws_data = data - if self.hass: - self.schedule_update_ha_state() - - @property - def device_id(self): - return self._deviceID diff --git a/custom_components/browser_mod/helpers.py b/custom_components/browser_mod/helpers.py new file mode 100644 index 0000000..18d9df1 --- /dev/null +++ b/custom_components/browser_mod/helpers.py @@ -0,0 +1,58 @@ +import logging + +from homeassistant.helpers.entity import Entity, async_generate_entity_id + +from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, DATA_ADDERS, CONFIG_DEVICES + +_LOGGER = logging.getLogger(__name__) + +def get_devices(hass): + return hass.data[DOMAIN][DATA_DEVICES] + +def get_alias(hass, deviceID): + for k,v in hass.data[DOMAIN][DATA_ALIASES].items(): + if v == deviceID: + return k + return None + +def create_entity(hass, platform, deviceID, connection): + _LOGGER.error("********************") + _LOGGER.error("Creating %s for %s", platform, deviceID) + adder = hass.data[DOMAIN][DATA_ADDERS][platform] + entity = adder(hass, deviceID, connection, get_alias(hass, deviceID)) + return entity + +def setup_platform(hass, config, async_add_devices, platform, cls): + def adder(hass, deviceID, connection, alias=None): + entity = cls(hass, connection, deviceID, alias) + async_add_devices([entity]) + return entity + hass.data[DOMAIN][DATA_ADDERS][platform] = adder + return True + +class BrowserModEntity(Entity): + + def __init__(self, hass, connection, deviceID, alias=None): + self.hass = hass + self.connection = connection + self.deviceID = deviceID + self._data = {} + self.entity_id = async_generate_entity_id(self.domain+".{}", alias or deviceID, hass=hass) + + def updated(self): + pass + + @property + def data(self): + return self._data + @data.setter + def data(self, data): + self._data = data + self.updated() + + @property + def device_id(self): + return self.deviceID + + def send(self, command, **kwargs): + self.connection.send(command, **kwargs) diff --git a/custom_components/browser_mod/light.py b/custom_components/browser_mod/light.py new file mode 100644 index 0000000..9fb182e --- /dev/null +++ b/custom_components/browser_mod/light.py @@ -0,0 +1,52 @@ +import logging +from datetime import datetime + +from homeassistant.const import STATE_UNAVAILABLE, STATE_ON, STATE_OFF +from homeassistant.components.light import Light, SUPPORT_BRIGHTNESS + +from .helpers import setup_platform, BrowserModEntity + +PLATFORM = 'light' + +async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModLight) + +class BrowserModLight(Light, BrowserModEntity): + domain = PLATFORM + + def __init__(self, hass, connection, deviceID, alias=None): + super().__init__(hass, connection, deviceID, alias) + + def updated(self): + self.last_seen = datetime.now() + self.schedule_update_ha_state() + + @property + def state(self): + if not self.connection.connection: + return STATE_UNAVAILABLE + if self.data.get('blackout', False): + return STATE_OFF + return STATE_ON + + @property + def is_on(self): + return not self.data.get('blackout', False) + + @property + def device_state_attributes(self): + return { + "type": "browser_mod", + } + + @property + def supported_features(self): + if self.data.get('brightness', False): + return SUPPORT_BRIGHTNESS + return 0 + + def turn_on(self, **kwargs): + self.connection.send("no-blackout", **kwargs) + + def turn_off(self, **kwargs): + self.connection.send("blackout") diff --git a/custom_components/browser_mod/media_player.py b/custom_components/browser_mod/media_player.py index c826380..ae74d26 100644 --- a/custom_components/browser_mod/media_player.py +++ b/custom_components/browser_mod/media_player.py @@ -13,48 +13,36 @@ from homeassistant.const import ( STATE_UNKNOWN, ) -from .const import DOMAIN, DATA_DEVICES, DATA_ADDERS, DATA_ALIASES -from .connection import BrowserModEntity +from .helpers import setup_platform, BrowserModEntity _LOGGER = logging.getLogger(__name__) +PLATFORM = 'media_player' async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): - def adder(hass, deviceID, connection, cid): - player = BrowserModPlayer(hass, deviceID) - if connection: - player.ws_connect(connection, cid) - async_add_devices([player]) - return player - hass.data[DOMAIN][DATA_ADDERS].append(adder) - - for k,v in hass.data[DOMAIN][DATA_ALIASES].items(): - devices = hass.data[DOMAIN][DATA_DEVICES] - devices[v] = BrowserModPlayer(hass, v, k) - async_add_devices([devices[v]]) + return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModPlayer) class BrowserModPlayer(MediaPlayerDevice, BrowserModEntity): + domain = PLATFORM - def __init__(self, hass, deviceID, alias=None): - super().__init__(hass, deviceID, alias) + def __init__(self, hass, connection, deviceID, alias=None): + super().__init__(hass, connection, deviceID, alias) + + def updated(self): + self.schedule_update_ha_state() @property def device_state_attributes(self): return { - "type": "browser", - **self._ws_data.get("browser", {}), + "type": "browser_mod", } - @property - def _player_data(self): - return self._ws_data.get("player", {}) - @property def state(self): - if not self._ws_connection: + if not self.connection.connection: return STATE_UNAVAILABLE - state = self._player_data.get("state", "unknown") + state = self.data.get("state", "unknown") return { "playing": STATE_PLAYING, "paused": STATE_PAUSED, @@ -69,24 +57,24 @@ class BrowserModPlayer(MediaPlayerDevice, BrowserModEntity): ) @property def volume_level(self): - return self._player_data.get("volume", 0) + return self.data.get("volume", 0) @property def is_volume_muted(self): - return self._player_data.get("muted", False) + return self.data.get("muted", False) @property def media_content_id(self): - return self._player_data.get("src", "") + return self.data.get("src", "") def set_volume_level(self, volume): - self.ws_send("set_volume", volume_level=volume) + self.connection.send("set_volume", volume_level=volume) def mute_volume(self, mute): - self.ws_send("mute", mute=mute) + self.connection.send("mute", mute=mute) def play_media(self, media_type, media_id, **kwargs): - self.ws_send("play", media_content_id=media_id) + self.connection.send("play", media_content_id=media_id) def media_play(self): - self.ws_send("play") + self.connection.send("play") def media_pause(self): - self.ws_send("pause") + self.connection.send("pause") def media_stop(self): - self.ws_send("stop") + self.connection.send("stop") diff --git a/custom_components/browser_mod/sensor.py b/custom_components/browser_mod/sensor.py new file mode 100644 index 0000000..94dff51 --- /dev/null +++ b/custom_components/browser_mod/sensor.py @@ -0,0 +1,36 @@ +import logging +from datetime import datetime + +from homeassistant.const import STATE_UNAVAILABLE + +from .helpers import setup_platform, BrowserModEntity + +PLATFORM = 'sensor' + +async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModSensor) + +class BrowserModSensor(BrowserModEntity): + domain = PLATFORM + + def __init__(self, hass, connection, deviceID, alias=None): + super().__init__(hass, connection, deviceID, alias) + self.last_seen = None + + def updated(self): + self.last_seen = datetime.now() + self.schedule_update_ha_state() + + @property + def state(self): + if not self.connection.connection: + return STATE_UNAVAILABLE + return len(self.connection.connection) + + @property + def device_state_attributes(self): + return { + "type": "browser_mod", + "last_seen": self.last_seen, + **self.data + } diff --git a/js/main.js b/js/main.js index ff06e44..558387f 100644 --- a/js/main.js +++ b/js/main.js @@ -84,6 +84,7 @@ class BrowserMod { conn.subscribeMessage((msg) => this.callback(msg), { type: 'browser_mod/connect', deviceID: deviceID, + fully: window.fully ? true : undefined, }); } @@ -267,12 +268,7 @@ class BrowserMod { visibility: document.visibilityState, userAgent: navigator.userAgent, currentUser: this._hass && this._hass.user && this._hass.user.name, - blackout: this.is_blackout(), fullyKiosk: window.fully ? true : undefined, - brightness: window.fully ? fully.getScreenBrightness() : undefined, - battery: window.fully ? fully.getBatteryLevel() : undefined, - charging: window.fully ? fully.isPlugged(): undefined, - motion: window.fully ? this._fullyMotion : undefined, }, player: { volume: this.player.volume, @@ -280,6 +276,15 @@ class BrowserMod { src: this.player.src, state: this.player_state, }, + screen: { + blackout: this.is_blackout(), + brightness: window.fully ? fully.getScreenBrightness() : undefined, + }, + fully: window.fully ? { + battery: window.fully ? fully.getBatteryLevel() : undefined, + charging: window.fully ? fully.isPlugged(): undefined, + motion: window.fully ? this._fullyMotion : undefined, + } : undefined, }, });