Compare commits

..

No commits in common. "1a3a7b2e953652d9c3b6dfa3f6d07cbe9fe11c00" and "79cb58175bead9d96ead01c7485cd5421693439c" have entirely different histories.

14 changed files with 109 additions and 174 deletions

View File

@ -1,31 +1,12 @@
# browser_mod 2.0 # browser_mod 2.0
[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs)
What if that tablet you have on your wall could open up a live feed from your front door camera when someone rings the bell?
And what if you could use it as an extra security camera?
Or what if you could use it to play music and videos from your Home Assistant media library?
What if you could permanently hide that sidebar from your kids and lock them into a single dashboard?
What if you could change the icon of the Home Assistant tab so it doesn't look the same as the forum?
What if you could change the more-info dialog for some entity to a dashboard card of your own design?
What if you could tap a button and have Home Assistant ask you which rooms you want the roomba to vacuum?
\
 
# Installation instructions # Installation instructions
- **First make sure you have completely removed any installation of Browser Mod 1** - **First make sure you have completely removed any installation of Browser Mod 1**
- Either - Either
- Find and install Browser Mod under `integrations`in [HACS](https://hacs.xyz) - ~~Find and install Browser Mod under `integrations`in [HACS](https://hacs.xyz)~~
- OR copy the contents of `custom_components/browser_mod/` to `<your config dir>/custom_components/browser_mod/`. - OR copy the contents of `custom_components/browser_mod/` to `<your config dir>/custom_components/browser_mod/`.
- Restart Home Assistant - Restart Home Assistant
@ -34,31 +15,49 @@ What if you could tap a button and have Home Assistant ask you which rooms you w
- Restart Home Assistant - Restart Home Assistant
> Note: If you are upgrading from Browser Mod 1, it is likely that you will get some errors in your log during a transition period. They will say something along the lines of `Error handling message: extra keys not allowed @ data['deviceID']`.
>
> They appear when a browser which has an old version of Browser Mod cached tries to connect and should disappear once you have cleared all your caches properly.
\
&nbsp;
# Browser Mod Configuration Panel # Browser Mod Configuration Panel
When you're logged in as an administrator you should see a new panel called _Browser Mod_ in the sidebar. This is where you controll any Browser Mod settings. When you're logged in as an administrator you should see a new panel called _Browser Mod_ in the sidebar. This is where you controll any Browser Mod settings.
### See [Configuration Panel](documentation/configuration-panel.md) for more info ## See [Configuration Panel](documentation/configuration-panel.md) for more info
\
&nbsp;
# Browser Mod Services # Browser Mod Services
Browser Mod has a number of services you can call to cause things to happen in the target Browser, such as opening a popup or navigating to a certain dashboard. Browser Mod has a number of services you can call to cause things to happen in the target Browser.
### See [Services](documentation/services.md) for more info ## See [Services](documentation/services.md) for more info
\
&nbsp;
### Calling services
## Popup card Services can be called from the backend using the normal service call procedures. Registered Browsers can be selected as targets through their device:
![A picture exemplifying setting up a browser_mod.more_info service call in the GUI editor](https://user-images.githubusercontent.com/1299821/180668350-1cbe751d-615d-4102-b939-e49e9cd2ca74.png)
In yaml, the BrowserID can be used for targeting a specific browser:
```yaml
service: browser_mod.more_info
data:
entity: light.bed_light
browser_id:
- 79be65e8-f06c78f
```
If no target or `browser_id` is specified, the service will target all registerd Browsers.
To call a service from a dashboard use the call-service [action](https://www.home-assistant.io/dashboards/actions/) or the special action `fire-dom-event`:
```yaml
tap_action:
action: fire-dom-event
browser_mod:
service: browser_mod.more_info
data:
entity: light.bed_light
```
Services called via `fire-dom-event` or called as a part of a different service call will (by default) _only_ target the current Browser (even if it's not registered).
# Popup card
A popup card can be used to replace the more-info dialog of an entity with something of your choosing. A popup card can be used to replace the more-info dialog of an entity with something of your choosing.
@ -81,7 +80,7 @@ card:
> *Note:* It's advisable to use a `fire-dom-event` tap action instead as far as possible. Popup card is for the few cases where that's not possible. See [`services`](documentation/services.md) for more info. > *Note:* It's advisable to use a `fire-dom-event` tap action instead as far as possible. Popup card is for the few cases where that's not possible. See [`services`](documentation/services.md) for more info.
## Browser Player # Browser Player
Browser player is a card that allows you to controll the volume and playback on the current Browsers media player. Browser player is a card that allows you to controll the volume and playback on the current Browsers media player.

View File

@ -167,16 +167,6 @@ class BrowserModBrowser:
device = dr.async_get_device({(DOMAIN, self.browserID)}) device = dr.async_get_device({(DOMAIN, self.browserID)})
dr.async_remove_device(device.id) dr.async_remove_device(device.id)
def get_device_id(self, hass):
er = entity_registry.async_get(hass)
entities = list(self.entities.values())
if len(entities):
entity = entities[0]
entry = er.async_get(entity.entity_id)
if entry:
return entry.device_id
return "default"
@property @property
def connection(self): def connection(self):
"""The current websocket connections for this Browser.""" """The current websocket connections for this Browser."""

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -62,9 +62,7 @@ async def async_setup_connection(hass):
dev.update_settings(hass, store.get_browser(browserID).asdict()) dev.update_settings(hass, store.get_browser(browserID).asdict())
dev.open_connection(connection, msg["id"]) dev.open_connection(connection, msg["id"])
await store.set_browser( await store.set_browser(
browserID, browserID, last_seen=datetime.now(tz=timezone.utc).isoformat()
last_seen=datetime.now(tz=timezone.utc).isoformat(),
meta=dev.get_device_id(hass),
) )
send_update(store.asdict()) send_update(store.asdict())

View File

@ -40,8 +40,8 @@ class BrowserModLight(BrowserModEntity, LightEntity):
def brightness(self): def brightness(self):
return self._data.get("screen_brightness", 1) return self._data.get("screen_brightness", 1)
async def async_turn_on(self, **kwargs): def turn_on(self, **kwargs):
await self.browser.send("screen_on", **kwargs) self.browser.send("screen_on", **kwargs)
async def async_turn_off(self, **kwargs): def turn_off(self, **kwargs):
await self.browser.send("screen_off") self.browser.send("screen_off")

View File

@ -5,7 +5,7 @@
"dependencies": ["panel_custom", "websocket_api", "http", "frontend", "lovelace"], "dependencies": ["panel_custom", "websocket_api", "http", "frontend", "lovelace"],
"codeowners": [], "codeowners": [],
"requirements": [], "requirements": [],
"version": "2.0.0", "version": "2.0.0b5",
"iot_class": "local_push", "iot_class": "local_push",
"config_flow": true "config_flow": true
} }

View File

@ -108,11 +108,11 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
def media_position_updated_at(self): def media_position_updated_at(self):
return dt.utcnow() return dt.utcnow()
async def async_set_volume_level(self, volume): def set_volume_level(self, volume):
await self.browser.send("player-set-volume", volume_level=volume) self.browser.send("player-set-volume", volume_level=volume)
async def async_mute_volume(self, mute): def mute_volume(self, mute):
await self.browser.send("player-mute", mute=mute) self.browser.send("player-mute", mute=mute)
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):
@ -124,7 +124,7 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
media_id = async_process_play_media_url(self.hass, media_id) 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)
await self.browser.send( self.browser.send(
"player-play", media_content_id=media_id, media_type=media_type, **kwargs "player-play", media_content_id=media_id, media_type=media_type, **kwargs
) )
@ -136,20 +136,20 @@ class BrowserModPlayer(BrowserModEntity, MediaPlayerEntity):
# content_filter=lambda item: item.media_content_type.startswith("audio/"), # content_filter=lambda item: item.media_content_type.startswith("audio/"),
) )
async def async_media_play(self): def media_play(self):
await self.browser.send("player-play") self.browser.send("player-play")
async def async_media_pause(self): def media_pause(self):
await self.browser.send("player-pause") self.browser.send("player-pause")
async def async_media_stop(self): def media_stop(self):
await self.browser.send("player-stop") self.browser.send("player-stop")
async def async_media_seek(self, position): def media_seek(self, position):
await self.browser.send("player-seek", position=position) self.browser.send("player-seek", position=position)
async def async_turn_off(self): def turn_off(self):
await self.browser.send("player-turn-off") self.browser.send("player-turn-off")
async def async_turn_on(self, **kwargs): def turn_on(self, **kwargs):
await self.browser.send("player-turn-on", **kwargs) self.browser.send("player-turn-on", **kwargs)

View File

@ -2,7 +2,7 @@
## This browser ## This browser
The most important concept for Browser Mod is the _Browser_. A _Browser_ is identified by a unique `BrowserID` stored in the browsers [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API). A basic concept for Browser Mod is the _Browser_. A _Browser_ is identified by a unique `BrowserID` stored in the browsers [LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API).
Browser Mod will initially assigning a random `BrowserID` to each _Browser_ that connects, but you can change this if you want. Browser Mod will initially assigning a random `BrowserID` to each _Browser_ that connects, but you can change this if you want.
@ -91,7 +91,7 @@ This will hide the header bar. Completely. It does not care if there are useful
Set the default dashboard that is shown when you access `https://<your home assistant url>/` with nothing after the `/`. Set the default dashboard that is shown when you access `https://<your home assistant url>/` with nothing after the `/`.
> *Note:* This also of works with other pages than lovelace dashboards, like e.g. `logbook` or even `history?device_id=f112fd806f2520c76318406f98cd244e&start_date=2022-09-02T16%3A00%3A00.000Z&end_date=2022-09-02T19%3A00%3A00.000Z`. > *Note:* This sort of works with other pages than lovelace dashboards, like e.g. `logbook` BUT there's currently a bug in Home Assistant which causes extra js modules (like browser_mod) to not load if you navigate to `https://<your home assistant url>/` and it does NOT redirect to a lovelace dashboard... Perhaps that will be fixed at some point. Perhaps you could live with it...
### Sidebar order ### Sidebar order

View File

@ -67,36 +67,6 @@ script:
Will print `"Button was clicked in 79be65e8-f06c78f" to the Home Assistant log. Will print `"Button was clicked in 79be65e8-f06c78f" to the Home Assistant log.
# Calling services
Services can be called from the backend using the normal service call procedures. Registered Browsers can be selected as targets through their device:
![A picture exemplifying setting up a browser_mod.more_info service call in the GUI editor](https://user-images.githubusercontent.com/1299821/180668350-1cbe751d-615d-4102-b939-e49e9cd2ca74.png)
In yaml, the BrowserID can be used for targeting a specific browser:
```yaml
service: browser_mod.more_info
data:
entity: light.bed_light
browser_id:
- 79be65e8-f06c78f
```
If no target or `browser_id` is specified, the service will target all registerd Browsers.
To call a service from a dashboard use the call-service [action](https://www.home-assistant.io/dashboards/actions/) or the special action `fire-dom-event`:
```yaml
tap_action:
action: fire-dom-event
browser_mod:
service: browser_mod.more_info
data:
entity: light.bed_light
```
Services called via `fire-dom-event` or called as a part of a different service call will (by default) _only_ target the current Browser (even if it's not registered).
# Browser Mod Services # Browser Mod Services

View File

@ -46,31 +46,21 @@ class BrowserModRegisteredBrowsersCard extends LitElement {
return html` return html`
<ha-card header="Registered Browsers" outlined> <ha-card header="Registered Browsers" outlined>
<div class="card-content"> <div class="card-content">
${Object.keys(window.browser_mod.browsers).map((d) => { ${Object.keys(window.browser_mod.browsers).map(
const browser = window.browser_mod.browsers[d]; (d) => html` <ha-settings-row>
return html` <ha-settings-row>
<span slot="heading"> ${d} </span> <span slot="heading"> ${d} </span>
<span slot="description"> <span slot="description">
Last connected: Last connected:
<ha-relative-time <ha-relative-time
.hass=${this.hass} .hass=${this.hass}
.datetime=${browser.last_seen} .datetime=${window.browser_mod.browsers[d].last_seen}
></ha-relative-time> ></ha-relative-time>
</span> </span>
${browser.meta && browser.meta !== "default"
? html`
<a href="config/devices/device/${browser.meta}">
<ha-icon-button>
<ha-icon .icon=${"mdi:devices"}></ha-icon>
</ha-icon-button>
</a>
`
: ""}
<ha-icon-button .browserID=${d} @click=${this.unregister_browser}> <ha-icon-button .browserID=${d} @click=${this.unregister_browser}>
<ha-icon .icon=${"mdi:delete"}></ha-icon> <ha-icon .icon=${"mdi:delete"}></ha-icon>
</ha-icon-button> </ha-icon-button>
</ha-settings-row>`; </ha-settings-row>`
})} )}
</div> </div>
${window.browser_mod.browsers["CAST"] === undefined ${window.browser_mod.browsers["CAST"] === undefined
? html` ? html`
@ -89,7 +79,6 @@ class BrowserModRegisteredBrowsersCard extends LitElement {
return css` return css`
ha-icon-button > * { ha-icon-button > * {
display: flex; display: flex;
color: var(--primary-text-color);
} }
`; `;
} }

View File

@ -28,7 +28,6 @@ class BrowserModPopup extends LitElement {
async closeDialog() { async closeDialog() {
this.open = false; this.open = false;
this.card = undefined;
clearInterval(this._timeoutTimer); clearInterval(this._timeoutTimer);
if (this._autocloseListener) { if (this._autocloseListener) {
window.browser_mod.removeEventListener( window.browser_mod.removeEventListener(
@ -320,7 +319,10 @@ class BrowserModPopup extends LitElement {
ha-dialog { ha-dialog {
--mdc-dialog-min-width: 100vw; --mdc-dialog-min-width: 100vw;
--mdc-dialog-max-width: 100vw; --mdc-dialog-max-width: 100vw;
--mdc-dialog-min-height: 100%;
--mdc-dialog-max-height: 100%;
--mdc-shape-medium: 0px; --mdc-shape-medium: 0px;
--vertial-align-dialog: flex-end;
} }
} }
`; `;
@ -342,13 +344,6 @@ export const PopupMixin = (SuperClass) => {
this._popupEl = document.createElement("browser-mod-popup"); this._popupEl = document.createElement("browser-mod-popup");
document.body.append(this._popupEl); document.body.append(this._popupEl);
this._popupEl.addEventListener("hass-more-info", async (ev) => {
const base = await hass_base_el();
console.log("More info", ev, base);
this._popupEl.closeDialog();
base.dispatchEvent(ev);
});
// const historyListener = async (ev) => { // const historyListener = async (ev) => {
// const popupState = ev.state?.browserModPopup; // const popupState = ev.state?.browserModPopup;
// if (popupState) { // if (popupState) {

View File

@ -64,7 +64,6 @@ export const RequireInteractMixin = (SuperClass) => {
this._interactionResolve(); this._interactionResolve();
} }
}); });
video.pause();
} }
window.addEventListener( window.addEventListener(

View File

@ -1,7 +1,7 @@
{ {
"name": "browser_mod", "name": "browser_mod",
"private": true, "private": true,
"version": "2.0.0", "version": "2.0.0b5",
"description": "", "description": "",
"scripts": { "scripts": {
"build": "rollup -c", "build": "rollup -c",