Compare commits
4 Commits
1dddaa9bcc
...
6e5a1c18a3
Author | SHA1 | Date | |
---|---|---|---|
6e5a1c18a3 | |||
df745e7f3b | |||
18eec919a0 | |||
0ecfe402ea |
@ -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):
|
||||||
|
@ -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"]
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user