Compare commits
No commits in common. "1a3a7b2e953652d9c3b6dfa3f6d07cbe9fe11c00" and "79cb58175bead9d96ead01c7485cd5421693439c" have entirely different histories.
1a3a7b2e95
...
79cb58175b
71
README.md
71
README.md
@ -1,31 +1,12 @@
|
|||||||
# browser_mod 2.0
|
# browser_mod 2.0
|
||||||
|
|
||||||
[](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.
|
|
||||||
|
|
||||||
\
|
|
||||||
|
|
||||||
|
|
||||||
# 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
|
||||||
\
|
|
||||||
|
|
||||||
|
|
||||||
# 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
|
||||||
\
|
|
||||||
|
|
||||||
|
|
||||||
|
### 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:
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
@ -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
@ -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())
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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:
|
|
||||||

|
|
||||||
|
|
||||||
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
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -64,7 +64,6 @@ export const RequireInteractMixin = (SuperClass) => {
|
|||||||
this._interactionResolve();
|
this._interactionResolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
video.pause();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
|
@ -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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user