Compare commits

...

4 Commits

Author SHA1 Message Date
6e5a1c18a3 Allow video playback in media_player. 2022-07-26 22:42:07 +00:00
df745e7f3b Finally fix popup closing? 2022-07-26 21:54:49 +00:00
18eec919a0 Make media player seekable 2022-07-26 20:32:58 +00:00
0ecfe402ea Set sensor icons 2022-07-26 19:24:21 +00:00
9 changed files with 261 additions and 73 deletions

View File

@ -1,6 +1,6 @@
from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.binary_sensor import BinarySensorEntity
from .const import DATA_BROWSERS, DOMAIN, DATA_ADDERS from .const import DOMAIN, DATA_ADDERS
from .entities import BrowserModEntity from .entities import BrowserModEntity
@ -15,14 +15,14 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class BrowserBinarySensor(BrowserModEntity, BinarySensorEntity): class BrowserBinarySensor(BrowserModEntity, BinarySensorEntity):
def __init__(self, coordinator, browserID, parameter, name): def __init__(self, coordinator, browserID, parameter, name, icon=None):
BrowserModEntity.__init__(self, coordinator, browserID, name) BrowserModEntity.__init__(self, coordinator, browserID, name, icon)
BinarySensorEntity.__init__(self) BinarySensorEntity.__init__(self)
self.parameter = parameter self.parameter = parameter
@property @property
def is_on(self): def is_on(self):
return self._data.get(DATA_BROWSERS, {}).get(self.parameter, None) return self._data.get("browser", {}).get(self.parameter, None)
class ActivityBinarySensor(BrowserModEntity, BinarySensorEntity): class ActivityBinarySensor(BrowserModEntity, BinarySensorEntity):

View File

@ -57,33 +57,50 @@ class BrowserModBrowser:
coordinator = self.coordinator coordinator = self.coordinator
browserID = self.browserID browserID = self.browserID
def _assert_browser_sensor(type, name, *properties): def _assert_browser_sensor(type, name, *properties, **kwarg):
"""Create a browser state sensor if it does not already exist""" """Create a browser state sensor if it does not already exist"""
if name in self.entities: if name in self.entities:
return return
adder = hass.data[DOMAIN][DATA_ADDERS][type] adder = hass.data[DOMAIN][DATA_ADDERS][type]
cls = {"sensor": BrowserSensor, "binary_sensor": BrowserBinarySensor}[type] cls = {"sensor": BrowserSensor, "binary_sensor": BrowserBinarySensor}[type]
new = cls(coordinator, browserID, name, *properties) new = cls(coordinator, browserID, name, *properties, **kwarg)
adder([new]) adder([new])
self.entities[name] = new self.entities[name] = new
_assert_browser_sensor("sensor", "path", "Browser path") _assert_browser_sensor("sensor", "path", "Browser path", icon="mdi:web")
_assert_browser_sensor("sensor", "visibility", "Browser visibility") _assert_browser_sensor("sensor", "visibility", "Browser visibility")
_assert_browser_sensor("sensor", "userAgent", "Browser userAgent") _assert_browser_sensor(
_assert_browser_sensor("sensor", "currentUser", "Browser user") "sensor", "userAgent", "Browser userAgent", icon="mdi:account-details"
_assert_browser_sensor("sensor", "width", "Browser width", "px") )
_assert_browser_sensor("sensor", "height", "Browser height", "px") _assert_browser_sensor(
"sensor", "currentUser", "Browser user", icon="mdi:account"
)
_assert_browser_sensor(
"sensor", "width", "Browser width", "px", icon="mdi:arrow-left-right"
)
_assert_browser_sensor(
"sensor", "height", "Browser height", "px", icon="mdi:arrow-up-down"
)
# Don't create battery sensor unless battery level is reported # Don't create battery sensor unless battery level is reported
if self.data.get("browser", {}).get("battery_level", None) is not None: if self.data.get("browser", {}).get("battery_level", None) is not None:
_assert_browser_sensor( _assert_browser_sensor(
"sensor", "battery_level", "Browser battery", "%", "battery" "sensor", "battery_level", "Browser battery", "%", "battery"
) )
_assert_browser_sensor("binary_sensor", "darkMode", "Browser dark mode") _assert_browser_sensor(
_assert_browser_sensor("binary_sensor", "fullyKiosk", "Browser FullyKiosk") "binary_sensor",
"darkMode",
"Browser dark mode",
icon="mdi:theme-light-dark",
)
_assert_browser_sensor(
"binary_sensor", "fullyKiosk", "Browser FullyKiosk", icon="mdi:alpha-f"
)
# Don't create a charging sensor unless charging state is reported # Don't create a charging sensor unless charging state is reported
if self.data.get("browser", {}).get("charging", None) is not None: if self.data.get("browser", {}).get("charging", None) is not None:
_assert_browser_sensor("binary_sensor", "charging", "Browser charging") _assert_browser_sensor(
"binary_sensor", "charging", "Browser charging", icon="mdi:power-plug"
)
if "activity" not in self.entities: if "activity" not in self.entities:
adder = hass.data[DOMAIN][DATA_ADDERS]["binary_sensor"] adder = hass.data[DOMAIN][DATA_ADDERS]["binary_sensor"]

View File

@ -619,10 +619,19 @@ const MediaPlayerMixin = (SuperClass) => {
return class MediaPlayerMixinClass extends SuperClass { return class MediaPlayerMixinClass extends SuperClass {
constructor() { constructor() {
super(); super();
this.player = new Audio(); this._audio_player = new Audio();
this._video_player = document.createElement("video");
this._video_player.controls = true;
this._video_player.style.setProperty("width", "100%");
this.player = this._audio_player;
this._player_enabled = false; this._player_enabled = false;
for (const ev of ["play", "pause", "ended", "volumechange"]) { for (const ev of ["play", "pause", "ended", "volumechange"]) {
this.player.addEventListener(ev, () => this._player_update()); this._audio_player.addEventListener(ev, () => this._player_update());
this._video_player.addEventListener(ev, () => this._player_update());
}
for (const ev of ["timeupdate"]) {
this._audio_player.addEventListener(ev, () => this._player_update_choked());
this._video_player.addEventListener(ev, () => this._player_update_choked());
} }
this.firstInteraction.then(() => { this.firstInteraction.then(() => {
this._player_enabled = true; this._player_enabled = true;
@ -630,10 +639,18 @@ const MediaPlayerMixin = (SuperClass) => {
this.player.play(); this.player.play();
}); });
this.addEventListener("command-player-play", (ev) => { this.addEventListener("command-player-play", (ev) => {
var _a; var _a, _b, _c;
if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_content_id) if (this.player.src)
this.player.pause();
if ((_a = ev.detail) === null || _a === void 0 ? void 0 : _a.media_type)
if ((_b = ev.detail) === null || _b === void 0 ? void 0 : _b.media_type.startsWith("video"))
this.player = this._video_player;
else
this.player = this._audio_player;
if ((_c = ev.detail) === null || _c === void 0 ? void 0 : _c.media_content_id)
this.player.src = ev.detail.media_content_id; this.player.src = ev.detail.media_content_id;
this.player.play(); this.player.play();
this._show_video_player();
}); });
this.addEventListener("command-player-pause", (ev) => this.player.pause()); this.addEventListener("command-player-pause", (ev) => this.player.pause());
this.addEventListener("command-player-stop", (ev) => { this.addEventListener("command-player-stop", (ev) => {
@ -653,17 +670,49 @@ const MediaPlayerMixin = (SuperClass) => {
else else
this.player.muted = !this.player.muted; this.player.muted = !this.player.muted;
}); });
this.addEventListener("command-player-seek", (ev) => {
this.player.currentTime = ev.detail.position;
setTimeout(() => this._player_update(), 10);
});
this.addEventListener("command-player-turn-off", (ev) => {
if (this.player === this._video_player &&
this._video_player.isConnected)
this.closePopup();
else if (this.player.src)
this.player.pause();
this.player.src = "";
this._player_update();
});
this.connectionPromise.then(() => this._player_update()); this.connectionPromise.then(() => this._player_update());
} }
_show_video_player() {
if (this.player === this._video_player && this.player.src) {
selectTree(document, "home-assistant $ dialog-media-player-browse").then((el) => el === null || el === void 0 ? void 0 : el.closeDialog());
this.showPopup(undefined, this._video_player, {
dismiss_action: () => this._video_player.pause(),
size: "wide",
});
}
else if (this.player !== this._video_player &&
this._video_player.isConnected) {
this.closePopup();
}
}
_player_update_choked() {
if (this._player_update_cooldown)
return;
this._player_update_cooldown = window.setTimeout(() => (this._player_update_cooldown = undefined), 3000);
this._player_update();
}
_player_update() { _player_update() {
const state = this._player_enabled const state = this._player_enabled
? this.player.src ? !this.player.src || this.player.src === window.location.href
? this.player.ended ? "off"
: this.player.ended
? "stopped" ? "stopped"
: this.player.paused : this.player.paused
? "paused" ? "paused"
: "playing" : "playing"
: "stopped"
: "unavailable"; : "unavailable";
this.sendUpdate({ this.sendUpdate({
player: { player: {
@ -671,6 +720,8 @@ const MediaPlayerMixin = (SuperClass) => {
muted: this.player.muted, muted: this.player.muted,
src: this.player.src, src: this.player.src,
state, state,
media_duration: this.player.duration,
media_position: this.player.currentTime,
}, },
}); });
} }
@ -1152,13 +1203,17 @@ class BrowserModPopup extends s {
} }
this._autocloseListener = undefined; this._autocloseListener = undefined;
if (this._autoclose) { if (this._autoclose) {
this._autocloseListener = this._dismiss.bind(this); this._autocloseListener = () => this.dialog.close();
window.browser_mod.addEventListener("browser-mod-activity", this._autocloseListener); window.browser_mod.addEventListener("browser-mod-activity", this._autocloseListener, { once: true });
} }
} }
async setupDialog(title, content, { right_button = undefined, right_button_action = undefined, left_button = undefined, left_button_action = undefined, dismissable = true, dismiss_action = undefined, timeout = undefined, timeout_action = undefined, size = undefined, style = undefined, autoclose = false, } = {}) { async setupDialog(title, content, { right_button = undefined, right_button_action = undefined, left_button = undefined, left_button_action = undefined, dismissable = true, dismiss_action = undefined, timeout = undefined, timeout_action = undefined, size = undefined, style = undefined, autoclose = false, } = {}) {
this.title = title; this.title = title;
if (content && typeof content === "object") { if (content && content instanceof HTMLElement) {
this.card = undefined;
this.content = content;
}
else if (content && typeof content === "object") {
// Create a card from config in content // Create a card from config in content
this.card = true; this.card = true;
const helpers = await window.loadCardHelpers(); const helpers = await window.loadCardHelpers();
@ -1189,30 +1244,30 @@ class BrowserModPopup extends s {
this._autoclose = autoclose; this._autoclose = autoclose;
} }
async _primary() { async _primary() {
var _a, _b, _c; var _a, _b, _c, _d;
if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action)
this._actions.dismiss_action = undefined; this._actions.dismiss_action = undefined;
await this.closeDialog(); (_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close();
(_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.right_button_action) === null || _c === void 0 ? void 0 : _c.call(_b); (_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.right_button_action) === null || _d === void 0 ? void 0 : _d.call(_c);
} }
async _secondary() { async _secondary() {
var _a, _b, _c; var _a, _b, _c, _d;
if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action)
this._actions.dismiss_action = undefined; this._actions.dismiss_action = undefined;
await this.closeDialog(); (_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close();
(_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.left_button_action) === null || _c === void 0 ? void 0 : _c.call(_b); (_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.left_button_action) === null || _d === void 0 ? void 0 : _d.call(_c);
} }
async _dismiss() { async _dismiss(ev) {
var _a, _b; var _a, _b, _c;
await this.closeDialog(); (_a = this.dialog) === null || _a === void 0 ? void 0 : _a.close();
(_b = (_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) === null || _b === void 0 ? void 0 : _b.call(_a); (_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.dismiss_action) === null || _c === void 0 ? void 0 : _c.call(_b);
} }
async _timeout() { async _timeout() {
var _a, _b, _c; var _a, _b, _c, _d;
if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action) if ((_a = this._actions) === null || _a === void 0 ? void 0 : _a.dismiss_action)
this._actions.dismiss_action = undefined; this._actions.dismiss_action = undefined;
await this.closeDialog(); (_b = this.dialog) === null || _b === void 0 ? void 0 : _b.close();
(_c = (_b = this._actions) === null || _b === void 0 ? void 0 : _b.timeout_action) === null || _c === void 0 ? void 0 : _c.call(_b); (_d = (_c = this._actions) === null || _c === void 0 ? void 0 : _c.timeout_action) === null || _d === void 0 ? void 0 : _d.call(_c);
} }
render() { render() {
if (!this.open) if (!this.open)
@ -1220,10 +1275,12 @@ class BrowserModPopup extends s {
return $ ` return $ `
<ha-dialog <ha-dialog
open open
@closed=${this.closeDialog}
@closing=${this._dismiss}
.heading=${this.title !== undefined} .heading=${this.title !== undefined}
?hideActions=${this.actions === undefined} ?hideActions=${this.actions === undefined}
.scrimClickAction=${this.dismissable ? this._dismiss : ""} .scrimClickAction=${this.dismissable ? "close" : ""}
.escapeKeyAction=${this.dismissable ? this._dismiss : ""} .escapeKeyAction=${this.dismissable ? "close" : ""}
> >
${this.timeout ${this.timeout
? $ ` <div slot="heading" class="progress"></div> ` ? $ ` <div slot="heading" class="progress"></div> `
@ -1274,6 +1331,7 @@ class BrowserModPopup extends s {
static get styles() { static get styles() {
return r$2 ` return r$2 `
ha-dialog { ha-dialog {
z-index: 10;
--mdc-dialog-min-width: var(--popup-min-width, 400px); --mdc-dialog-min-width: var(--popup-min-width, 400px);
--mdc-dialog-max-width: var(--popup-max-width, 600px); --mdc-dialog-max-width: var(--popup-max-width, 600px);
--mdc-dialog-heading-ink-color: var(--primary-text-color); --mdc-dialog-heading-ink-color: var(--primary-text-color);
@ -2165,8 +2223,8 @@ const BrowserIDMixin = (SuperClass) => {
- Tweaks - Tweaks
- Quickbar tweaks (ctrl+enter)? - Quickbar tweaks (ctrl+enter)?
x Card-mod preload x Card-mod preload
- Video player? x Video player?
- Media_seek x Media_seek
- Screensavers - Screensavers
x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES
- NOFIX. Home Assistant bug - NOFIX. Home Assistant bug

View File

@ -11,10 +11,11 @@ _LOGGER = logging.getLogger(__name__)
class BrowserModEntity(CoordinatorEntity): class BrowserModEntity(CoordinatorEntity):
def __init__(self, coordinator, browserID, name): def __init__(self, coordinator, browserID, name, icon=None):
super().__init__(coordinator) super().__init__(coordinator)
self.browserID = browserID self.browserID = browserID
self._name = name self._name = name
self._icon = icon
@property @property
def _data(self): def _data(self):
@ -54,3 +55,7 @@ class BrowserModEntity(CoordinatorEntity):
@property @property
def unique_id(self): def unique_id(self):
return f"{self.browserID}-{self._name.replace(' ','_')}" return f"{self.browserID}-{self._name.replace(' ','_')}"
@property
def icon(self):
return self._icon

View File

@ -15,6 +15,9 @@ from homeassistant.components.media_player.const import (
MEDIA_TYPE_MUSIC, MEDIA_TYPE_MUSIC,
MEDIA_TYPE_URL, MEDIA_TYPE_URL,
SUPPORT_BROWSE_MEDIA, SUPPORT_BROWSE_MEDIA,
SUPPORT_SEEK,
SUPPORT_TURN_OFF,
SUPPORT_TURN_ON,
) )
from homeassistant.const import ( from homeassistant.const import (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
@ -22,8 +25,12 @@ from homeassistant.const import (
STATE_PLAYING, STATE_PLAYING,
STATE_IDLE, STATE_IDLE,
STATE_UNKNOWN, STATE_UNKNOWN,
STATE_ON,
STATE_OFF,
) )
from homeassistant.util import dt
from .entities import BrowserModEntity from .entities import BrowserModEntity
from .const import DOMAIN, DATA_ADDERS from .const import DOMAIN, DATA_ADDERS
@ -60,6 +67,8 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
"paused": STATE_PAUSED, "paused": STATE_PAUSED,
"stopped": STATE_IDLE, "stopped": STATE_IDLE,
"unavailable": STATE_UNAVAILABLE, "unavailable": STATE_UNAVAILABLE,
"on": STATE_ON,
"off": STATE_OFF,
}.get(state, STATE_UNKNOWN) }.get(state, STATE_UNKNOWN)
@property @property
@ -72,6 +81,9 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
| SUPPORT_VOLUME_SET | SUPPORT_VOLUME_SET
| SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_MUTE
| SUPPORT_BROWSE_MEDIA | SUPPORT_BROWSE_MEDIA
| SUPPORT_SEEK
| SUPPORT_TURN_OFF
| SUPPORT_TURN_ON
) )
@property @property
@ -82,6 +94,20 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
def is_volume_muted(self): def is_volume_muted(self):
return self._data.get("player", {}).get("muted", False) return self._data.get("player", {}).get("muted", False)
@property
def media_duration(self):
duration = self._data.get("player", {}).get("media_duration", None)
return float(duration) if duration is not None else None
@property
def media_position(self):
position = self._data.get("player", {}).get("media_position", None)
return float(position) if position is not None else None
@property
def media_position_updated_at(self):
return dt.utcnow()
def set_volume_level(self, volume): def set_volume_level(self, volume):
self.browser.send("player-set-volume", volume_level=volume) self.browser.send("player-set-volume", volume_level=volume)
@ -90,21 +116,24 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
async def async_play_media(self, media_type, media_id, **kwargs): async def async_play_media(self, media_type, media_id, **kwargs):
if media_source.is_media_source_id(media_id): if media_source.is_media_source_id(media_id):
media_type = MEDIA_TYPE_URL
play_item = await media_source.async_resolve_media( play_item = await media_source.async_resolve_media(
self.hass, media_id, self.entity_id self.hass, media_id, self.entity_id
) )
media_type = play_item.mime_type
media_id = play_item.url media_id = play_item.url
media_id = async_process_play_media_url(self.hass, media_id)
if media_type in (MEDIA_TYPE_URL, MEDIA_TYPE_MUSIC): if media_type in (MEDIA_TYPE_URL, MEDIA_TYPE_MUSIC):
media_id = async_process_play_media_url(self.hass, media_id) media_id = async_process_play_media_url(self.hass, media_id)
self.browser.send("player-play", media_content_id=media_id) self.browser.send(
"player-play", media_content_id=media_id, media_type=media_type, **kwargs
)
async def async_browse_media(self, media_content_type=None, media_content_id=None): async def async_browse_media(self, media_content_type=None, media_content_id=None):
"""Implement the websocket media browsing helper.""" """Implement the websocket media browsing helper."""
return await media_source.async_browse_media( return await media_source.async_browse_media(
self.hass, self.hass,
media_content_id, media_content_id,
content_filter=lambda item: item.media_content_type.startswith("audio/"), # content_filter=lambda item: item.media_content_type.startswith("audio/"),
) )
def media_play(self): def media_play(self):
@ -115,3 +144,12 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
def media_stop(self): def media_stop(self):
self.browser.send("player-stop") self.browser.send("player-stop")
def media_seek(self, position):
self.browser.send("player-seek", position=position)
def turn_off(self):
self.browser.send("player-turn-off")
def turn_on(self, **kwargs):
self.browser.send("player-turn-on", **kwargs)

View File

@ -23,8 +23,9 @@ class BrowserSensor(BrowserModEntity, SensorEntity):
name, name,
unit_of_measurement=None, unit_of_measurement=None,
device_class=None, device_class=None,
icon=None,
): ):
BrowserModEntity.__init__(self, coordinator, browserID, name) BrowserModEntity.__init__(self, coordinator, browserID, name, icon)
SensorEntity.__init__(self) SensorEntity.__init__(self)
self.parameter = parameter self.parameter = parameter
self._device_class = device_class self._device_class = device_class
@ -32,10 +33,7 @@ class BrowserSensor(BrowserModEntity, SensorEntity):
@property @property
def native_value(self): def native_value(self):
data = self._data return self._data.get("browser", {}).get(self.parameter, None)
data = data.get("browser", {})
data = data.get(self.parameter, None)
return data
@property @property
def device_class(self): def device_class(self):

View File

@ -67,8 +67,8 @@ import { BrowserIDMixin } from "./browserID";
- Tweaks - Tweaks
- Quickbar tweaks (ctrl+enter)? - Quickbar tweaks (ctrl+enter)?
x Card-mod preload x Card-mod preload
- Video player? x Video player?
- Media_seek x Media_seek
- Screensavers - Screensavers
x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES x IMPORTANT: FIX DEFAULT HIDING OF ENTITIES
- NOFIX. Home Assistant bug - NOFIX. Home Assistant bug

View File

@ -1,16 +1,34 @@
import { selectTree } from "../helpers";
export const MediaPlayerMixin = (SuperClass) => { export const MediaPlayerMixin = (SuperClass) => {
return class MediaPlayerMixinClass extends SuperClass { return class MediaPlayerMixinClass extends SuperClass {
public player; public player;
private _audio_player;
private _video_player;
private _player_enabled; private _player_enabled;
private _player_update_cooldown;
constructor() { constructor() {
super(); super();
this.player = new Audio(); this._audio_player = new Audio();
this._video_player = document.createElement("video");
this._video_player.controls = true;
this._video_player.style.setProperty("width", "100%");
this.player = this._audio_player;
this._player_enabled = false; this._player_enabled = false;
for (const ev of ["play", "pause", "ended", "volumechange"]) { for (const ev of ["play", "pause", "ended", "volumechange"]) {
this.player.addEventListener(ev, () => this._player_update()); this._audio_player.addEventListener(ev, () => this._player_update());
this._video_player.addEventListener(ev, () => this._player_update());
}
for (const ev of ["timeupdate"]) {
this._audio_player.addEventListener(ev, () =>
this._player_update_choked()
);
this._video_player.addEventListener(ev, () =>
this._player_update_choked()
);
} }
this.firstInteraction.then(() => { this.firstInteraction.then(() => {
@ -19,9 +37,15 @@ export const MediaPlayerMixin = (SuperClass) => {
}); });
this.addEventListener("command-player-play", (ev) => { this.addEventListener("command-player-play", (ev) => {
if (this.player.src) this.player.pause();
if (ev.detail?.media_type)
if (ev.detail?.media_type.startsWith("video"))
this.player = this._video_player;
else this.player = this._audio_player;
if (ev.detail?.media_content_id) if (ev.detail?.media_content_id)
this.player.src = ev.detail.media_content_id; this.player.src = ev.detail.media_content_id;
this.player.play(); this.player.play();
this._show_video_player();
}); });
this.addEventListener("command-player-pause", (ev) => this.addEventListener("command-player-pause", (ev) =>
this.player.pause() this.player.pause()
@ -39,19 +63,60 @@ export const MediaPlayerMixin = (SuperClass) => {
this.player.muted = Boolean(ev.detail.mute); this.player.muted = Boolean(ev.detail.mute);
else this.player.muted = !this.player.muted; else this.player.muted = !this.player.muted;
}); });
this.addEventListener("command-player-seek", (ev) => {
this.player.currentTime = ev.detail.position;
setTimeout(() => this._player_update(), 10);
});
this.addEventListener("command-player-turn-off", (ev) => {
if (
this.player === this._video_player &&
this._video_player.isConnected
)
this.closePopup();
else if (this.player.src) this.player.pause();
this.player.src = "";
this._player_update();
});
this.connectionPromise.then(() => this._player_update()); this.connectionPromise.then(() => this._player_update());
} }
private _show_video_player() {
if (this.player === this._video_player && this.player.src) {
selectTree(
document,
"home-assistant $ dialog-media-player-browse"
).then((el) => el?.closeDialog());
this.showPopup(undefined, this._video_player, {
dismiss_action: () => this._video_player.pause(),
size: "wide",
});
} else if (
this.player !== this._video_player &&
this._video_player.isConnected
) {
this.closePopup();
}
}
private _player_update_choked() {
if (this._player_update_cooldown) return;
this._player_update_cooldown = window.setTimeout(
() => (this._player_update_cooldown = undefined),
3000
);
this._player_update();
}
private _player_update() { private _player_update() {
const state = this._player_enabled const state = this._player_enabled
? this.player.src ? !this.player.src || this.player.src === window.location.href
? this.player.ended ? "off"
? "stopped" : this.player.ended
: this.player.paused ? "stopped"
? "paused" : this.player.paused
: "playing" ? "paused"
: "stopped" : "playing"
: "unavailable"; : "unavailable";
this.sendUpdate({ this.sendUpdate({
player: { player: {
@ -59,6 +124,8 @@ export const MediaPlayerMixin = (SuperClass) => {
muted: this.player.muted, muted: this.player.muted,
src: this.player.src, src: this.player.src,
state, state,
media_duration: this.player.duration,
media_position: this.player.currentTime,
}, },
}); });
} }

View File

@ -3,8 +3,6 @@ import { property, query } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { unsafeHTML } from "lit/directives/unsafe-html.js";
import { provideHass, loadLoadCardHelpers, hass_base_el } from "../helpers"; import { provideHass, loadLoadCardHelpers, hass_base_el } from "../helpers";
let aaa = 0;
class BrowserModPopup extends LitElement { class BrowserModPopup extends LitElement {
@property() open; @property() open;
@property() content; @property() content;
@ -52,10 +50,11 @@ class BrowserModPopup extends LitElement {
} }
this._autocloseListener = undefined; this._autocloseListener = undefined;
if (this._autoclose) { if (this._autoclose) {
this._autocloseListener = this._dismiss.bind(this); this._autocloseListener = () => this.dialog.close();
window.browser_mod.addEventListener( window.browser_mod.addEventListener(
"browser-mod-activity", "browser-mod-activity",
this._autocloseListener this._autocloseListener,
{ once: true }
); );
} }
} }
@ -78,7 +77,10 @@ class BrowserModPopup extends LitElement {
} = {} } = {}
) { ) {
this.title = title; this.title = title;
if (content && typeof content === "object") { if (content && content instanceof HTMLElement) {
this.card = undefined;
this.content = content;
} else if (content && typeof content === "object") {
// Create a card from config in content // Create a card from config in content
this.card = true; this.card = true;
const helpers = await window.loadCardHelpers(); const helpers = await window.loadCardHelpers();
@ -112,21 +114,21 @@ class BrowserModPopup extends LitElement {
async _primary() { async _primary() {
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined; if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
await this.closeDialog(); this.dialog?.close();
this._actions?.right_button_action?.(); this._actions?.right_button_action?.();
} }
async _secondary() { async _secondary() {
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined; if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
await this.closeDialog(); this.dialog?.close();
this._actions?.left_button_action?.(); this._actions?.left_button_action?.();
} }
async _dismiss() { async _dismiss(ev?) {
await this.closeDialog(); this.dialog?.close();
this._actions?.dismiss_action?.(); this._actions?.dismiss_action?.();
} }
async _timeout() { async _timeout() {
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined; if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
await this.closeDialog(); this.dialog?.close();
this._actions?.timeout_action?.(); this._actions?.timeout_action?.();
} }
@ -136,10 +138,12 @@ class BrowserModPopup extends LitElement {
return html` return html`
<ha-dialog <ha-dialog
open open
@closed=${this.closeDialog}
@closing=${this._dismiss}
.heading=${this.title !== undefined} .heading=${this.title !== undefined}
?hideActions=${this.actions === undefined} ?hideActions=${this.actions === undefined}
.scrimClickAction=${this.dismissable ? this._dismiss : ""} .scrimClickAction=${this.dismissable ? "close" : ""}
.escapeKeyAction=${this.dismissable ? this._dismiss : ""} .escapeKeyAction=${this.dismissable ? "close" : ""}
> >
${this.timeout ${this.timeout
? html` <div slot="heading" class="progress"></div> ` ? html` <div slot="heading" class="progress"></div> `
@ -191,6 +195,7 @@ class BrowserModPopup extends LitElement {
static get styles() { static get styles() {
return css` return css`
ha-dialog { ha-dialog {
z-index: 10;
--mdc-dialog-min-width: var(--popup-min-width, 400px); --mdc-dialog-min-width: var(--popup-min-width, 400px);
--mdc-dialog-max-width: var(--popup-max-width, 600px); --mdc-dialog-max-width: var(--popup-max-width, 600px);
--mdc-dialog-heading-ink-color: var(--primary-text-color); --mdc-dialog-heading-ink-color: var(--primary-text-color);