Fork sync
This commit is contained in:
commit
c6b6a0c5d9
45
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
45
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: For reporting bugs or unexpected behavior
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
My Home Assistant version: 0.XX.X
|
||||||
|
|
||||||
|
What I am doing:
|
||||||
|
|
||||||
|
|
||||||
|
What I expected to happen:
|
||||||
|
|
||||||
|
|
||||||
|
What happened instead:
|
||||||
|
|
||||||
|
|
||||||
|
**Minimal** steps to reproduce:
|
||||||
|
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# The least ammount of code possible to reproduce my error
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# End of code
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Error messages from the browser console:
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**By putting an X in the boxes ([ ]) below, I indicate that I:**
|
||||||
|
|
||||||
|
- [ ] Understand that this is a channel for reporting bugs, not a support forum (https://community.home-assistant.io/).
|
||||||
|
|
||||||
|
- [ ] Have made sure I am using the latest version of the plugin.
|
||||||
|
|
||||||
|
- [ ] Have followed the troubleshooting steps of the "Common Problems" section of https://github.com/thomasloven/hass-config/wiki/Lovelace-Plugins.
|
||||||
|
|
||||||
|
- [ ] Understand that leaving one or more boxes unticked or failure to follow the template above may increase the time required to handle my bug-report, or cause it to be closed without further action.
|
7
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
7
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: For suggesting new features
|
||||||
|
title: ''
|
||||||
|
labels: 'feature-request'
|
||||||
|
assignees: ''
|
||||||
|
---
|
9
.github/stale.yml
vendored
Normal file
9
.github/stale.yml
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
daysUntilStale: 60
|
||||||
|
daysUntilClose: 7
|
||||||
|
exemptLabels:
|
||||||
|
- pinned
|
||||||
|
- feature-request
|
||||||
|
staleLabel: stale
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
|
||||||
|
closeComment: false
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
**/__pycache__/
|
**/__pycache__/
|
||||||
|
.vscode
|
||||||
|
.env
|
||||||
|
14
README.md
14
README.md
@ -72,10 +72,14 @@ This binds the *aliases* `arrakis` to `99980b13-dabc9563` and `dashboard` to `d2
|
|||||||
Note: Aliases must be unique.
|
Note: Aliases must be unique.
|
||||||
|
|
||||||
|
|
||||||
#### Experimental: Custom deviceID
|
#### Changing deviceID
|
||||||
|
You can change the deviceID of your device by adding a `browser-player` card to your lovelace interface and clicking the deviceID at the bottom of the card. Set it to `clear` to generate a new random one.
|
||||||
|
|
||||||
You can also set a deviceID by adding `?deviceID=mydeviceID` to the end of the URL you're using to access Home Assistant. Be careful - I have no idea what could happen if several devices were to have the same ID.
|
You can also set a deviceID by adding `?deviceID=mydeviceID` to the end of the URL you're using to access Home Assistant. Be careful - I have no idea what could happen if several devices were to have the same ID.
|
||||||
Use `?deviceID=clear` to generate a new random one.
|
Use `?deviceID=clear` to generate a new random one.
|
||||||
|
|
||||||
|
**Take care to avoid deviceID collissions. There's no telling what could happen if more devices share the same ID.**
|
||||||
|
|
||||||
### Prefix
|
### Prefix
|
||||||
You can add a custom prefix to all entity ids in `configuration.yaml`:
|
You can add a custom prefix to all entity ids in `configuration.yaml`:
|
||||||
|
|
||||||
@ -137,6 +141,8 @@ The sensor also has the following attributes:
|
|||||||
| `fullyKiosk` | True if the *device* is a Fully Kiosk browser. Undefined otherwise. |
|
| `fullyKiosk` | True if the *device* is a Fully Kiosk browser. Undefined otherwise. |
|
||||||
| `width` | The current width of the browser window in pixels. |
|
| `width` | The current width of the browser window in pixels. |
|
||||||
| `height` | The current height of the browser window in pixels. |
|
| `height` | The current height of the browser window in pixels. |
|
||||||
|
| `battery_level` | The current battery level of your device - if supported |
|
||||||
|
| `charging` | The current charging state of your device - if supported |
|
||||||
|
|
||||||
### media\_player
|
### media\_player
|
||||||
|
|
||||||
@ -150,7 +156,7 @@ The `light` can be used to blackout the screen.
|
|||||||
For Fully Kiosk Browser, the screen will actually turn off.
|
For Fully Kiosk Browser, the screen will actually turn off.
|
||||||
For other browsers, the interface will just be covered with black (the screen is still on, will have a visible glow in the dark, and you won't save any battery).
|
For other browsers, the interface will just be covered with black (the screen is still on, will have a visible glow in the dark, and you won't save any battery).
|
||||||
|
|
||||||
### camera (EXPERIMENTAL)
|
### camera
|
||||||
|
|
||||||
For security and UX reasons, the camera must be enabled manually on a device by device basis.
|
For security and UX reasons, the camera must be enabled manually on a device by device basis.
|
||||||
|
|
||||||
@ -179,7 +185,7 @@ It's state will be the state of the camera motion detector of the *device* (5 se
|
|||||||
|
|
||||||
`browser_mod` registers a number of services.
|
`browser_mod` registers a number of services.
|
||||||
|
|
||||||
All service calls have one parameter in common; `deviceID` which is a list of *devices* to execute the comand on. If `deviceID` is omitted, the command will be executed on **all** currenctly connected *devices*. `deviceID` may also contain aliases.
|
All service calls have one parameter in common; `deviceID` which is a list of *devices* to execute the comand on. If `deviceID` is omitted, the command will be executed on **all** currently connected *devices*. `deviceID` may also contain aliases.
|
||||||
|
|
||||||
If a service is called from the frontend (e.g. by using the `call-service` tap action), a value of `this` in the `deviceID` list will be replaced with the ID of the *device* the call was made from.
|
If a service is called from the frontend (e.g. by using the `call-service` tap action), a value of `this` in the `deviceID` list will be replaced with the ID of the *device* the call was made from.
|
||||||
Alternatively, `deviceID: this` will also work.
|
Alternatively, `deviceID: this` will also work.
|
||||||
@ -333,7 +339,7 @@ Second, there are a few more attributes available
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `fullyKiosk` | True. |
|
| `fullyKiosk` | True. |
|
||||||
| `brightness` | The current screen brightness. |
|
| `brightness` | The current screen brightness. |
|
||||||
| `battery` | The current charge percentage of the devices battery. |
|
| `battery_level` | The current charge percentage of the devices battery. |
|
||||||
| `charging` | Whether the battery is currently charging. |
|
| `charging` | Whether the battery is currently charging. |
|
||||||
| `motion` | Whether the devices camera has detected any motion in the last five seconds. |
|
| `motion` | Whether the devices camera has detected any motion in the last five seconds. |
|
||||||
|
|
||||||
|
@ -3,39 +3,50 @@ import logging
|
|||||||
from .mod_view import setup_view
|
from .mod_view import setup_view
|
||||||
from .connection import setup_connection
|
from .connection import setup_connection
|
||||||
from .service import setup_service
|
from .service import setup_service
|
||||||
from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, DATA_ADDERS, CONFIG_DEVICES, DATA_CONFIG
|
from .const import (
|
||||||
|
DOMAIN,
|
||||||
|
DATA_DEVICES,
|
||||||
|
DATA_ALIASES,
|
||||||
|
DATA_ADDERS,
|
||||||
|
CONFIG_DEVICES,
|
||||||
|
DATA_CONFIG,
|
||||||
|
DATA_SETUP_COMPLETE,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
|
||||||
|
|
||||||
await setup_connection(hass, config)
|
async def async_setup(hass, config):
|
||||||
setup_view(hass)
|
|
||||||
|
|
||||||
aliases = {}
|
aliases = {}
|
||||||
for d in config[DOMAIN].get(CONFIG_DEVICES, {}):
|
for d in config[DOMAIN].get(CONFIG_DEVICES, {}):
|
||||||
name = config[DOMAIN][CONFIG_DEVICES][d].get("name", None)
|
name = config[DOMAIN][CONFIG_DEVICES][d].get("name", None)
|
||||||
if name:
|
if name:
|
||||||
aliases[name] = d.replace('_','-')
|
aliases[name] = d.replace('_', '-')
|
||||||
|
|
||||||
hass.data[DOMAIN] = {
|
hass.data[DOMAIN] = {
|
||||||
DATA_DEVICES: {},
|
DATA_DEVICES: {},
|
||||||
DATA_ALIASES: aliases,
|
DATA_ALIASES: aliases,
|
||||||
DATA_ADDERS: {},
|
DATA_ADDERS: {},
|
||||||
DATA_CONFIG: config[DOMAIN],
|
DATA_CONFIG: config[DOMAIN],
|
||||||
|
DATA_SETUP_COMPLETE: False,
|
||||||
}
|
}
|
||||||
|
|
||||||
await hass.helpers.discovery.async_load_platform("media_player", DOMAIN, {}, config)
|
await setup_connection(hass, config)
|
||||||
await hass.helpers.discovery.async_load_platform("sensor", DOMAIN, {}, config)
|
setup_view(hass)
|
||||||
await hass.helpers.discovery.async_load_platform("binary_sensor", DOMAIN, {}, config)
|
|
||||||
await hass.helpers.discovery.async_load_platform("light", DOMAIN, {}, config)
|
|
||||||
await hass.helpers.discovery.async_load_platform("camera", DOMAIN, {}, config)
|
|
||||||
|
|
||||||
|
async_load_platform = hass.helpers.discovery.async_load_platform
|
||||||
|
await async_load_platform("media_player", DOMAIN, {}, config)
|
||||||
|
await async_load_platform("sensor", DOMAIN, {}, config)
|
||||||
|
await async_load_platform("binary_sensor", DOMAIN, {}, config)
|
||||||
|
await async_load_platform("light", DOMAIN, {}, config)
|
||||||
|
await async_load_platform("camera", DOMAIN, {}, config)
|
||||||
|
|
||||||
await setup_service(hass)
|
await setup_service(hass)
|
||||||
|
|
||||||
|
hass.data[DOMAIN][DATA_SETUP_COMPLETE] = True
|
||||||
|
|
||||||
|
for device in hass.data[DOMAIN][DATA_DEVICES].values():
|
||||||
|
device.trigger_update()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@ -21,7 +21,8 @@ class BrowserModCamera(Camera, BrowserModEntity):
|
|||||||
self.last_seen = None
|
self.last_seen = None
|
||||||
|
|
||||||
def updated(self):
|
def updated(self):
|
||||||
self.last_seen = datetime.now()
|
if self.last_seen is None or (datetime.now() - self.last_seen).seconds > 15:
|
||||||
|
self.last_seen = datetime.now()
|
||||||
self.schedule_update_ha_state()
|
self.schedule_update_ha_state()
|
||||||
|
|
||||||
def camera_image(self):
|
def camera_image(self):
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import logging
|
import logging
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.websocket_api import websocket_command, result_message, event_message, async_register_command
|
from homeassistant.components.websocket_api import (
|
||||||
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
websocket_command,
|
||||||
|
result_message,
|
||||||
|
event_message,
|
||||||
|
async_register_command
|
||||||
|
)
|
||||||
|
|
||||||
from .const import DOMAIN, WS_CONNECT, WS_UPDATE, WS_CAMERA
|
from .const import WS_CONNECT, WS_UPDATE
|
||||||
from .helpers import get_devices, create_entity, get_config
|
from .helpers import get_devices, create_entity, get_config, is_setup_complete
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def setup_connection(hass, config):
|
async def setup_connection(hass, config):
|
||||||
|
|
||||||
@websocket_command({
|
@websocket_command({
|
||||||
@ -18,7 +23,8 @@ async def setup_connection(hass, config):
|
|||||||
def handle_connect(hass, connection, msg):
|
def handle_connect(hass, connection, msg):
|
||||||
deviceID = msg["deviceID"]
|
deviceID = msg["deviceID"]
|
||||||
|
|
||||||
device = get_devices(hass).get(deviceID, BrowserModConnection(hass, deviceID))
|
device = get_devices(hass).get(deviceID,
|
||||||
|
BrowserModConnection(hass, deviceID))
|
||||||
device.connect(connection, msg["id"])
|
device.connect(connection, msg["id"])
|
||||||
get_devices(hass)[deviceID] = device
|
get_devices(hass)[deviceID] = device
|
||||||
|
|
||||||
@ -29,7 +35,7 @@ async def setup_connection(hass, config):
|
|||||||
vol.Required("deviceID"): str,
|
vol.Required("deviceID"): str,
|
||||||
vol.Optional("data"): dict,
|
vol.Optional("data"): dict,
|
||||||
})
|
})
|
||||||
def handle_update( hass, connection, msg):
|
def handle_update(hass, connection, msg):
|
||||||
devices = get_devices(hass)
|
devices = get_devices(hass)
|
||||||
deviceID = msg["deviceID"]
|
deviceID = msg["deviceID"]
|
||||||
if deviceID in devices:
|
if deviceID in devices:
|
||||||
@ -38,6 +44,7 @@ async def setup_connection(hass, config):
|
|||||||
async_register_command(hass, handle_connect)
|
async_register_command(hass, handle_connect)
|
||||||
async_register_command(hass, handle_update)
|
async_register_command(hass, handle_update)
|
||||||
|
|
||||||
|
|
||||||
class BrowserModConnection:
|
class BrowserModConnection:
|
||||||
def __init__(self, hass, deviceID):
|
def __init__(self, hass, deviceID):
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
@ -52,7 +59,7 @@ class BrowserModConnection:
|
|||||||
|
|
||||||
def connect(self, connection, cid):
|
def connect(self, connection, cid):
|
||||||
self.connection.append((connection, cid))
|
self.connection.append((connection, cid))
|
||||||
self.send("update", **get_config(self.hass, self.deviceID))
|
self.trigger_update()
|
||||||
|
|
||||||
def disconnect():
|
def disconnect():
|
||||||
self.connection.remove((connection, cid))
|
self.connection.remove((connection, cid))
|
||||||
@ -67,6 +74,10 @@ class BrowserModConnection:
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
def trigger_update(self):
|
||||||
|
if is_setup_complete(self.hass):
|
||||||
|
self.send("update", **get_config(self.hass, self.deviceID))
|
||||||
|
|
||||||
def update(self, data):
|
def update(self, data):
|
||||||
if data.get('browser'):
|
if data.get('browser'):
|
||||||
self.sensor = self.sensor or create_entity(
|
self.sensor = self.sensor or create_entity(
|
||||||
@ -112,4 +123,3 @@ class BrowserModConnection:
|
|||||||
self)
|
self)
|
||||||
if self.camera:
|
if self.camera:
|
||||||
self.camera.data = data.get('camera')
|
self.camera.data = data.get('camera')
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ DATA_DEVICES = "devices"
|
|||||||
DATA_ALIASES = "aliases"
|
DATA_ALIASES = "aliases"
|
||||||
DATA_ADDERS = "adders"
|
DATA_ADDERS = "adders"
|
||||||
DATA_CONFIG = "config"
|
DATA_CONFIG = "config"
|
||||||
|
DATA_SETUP_COMPLETE = "setup_complete"
|
||||||
|
|
||||||
CONFIG_DEVICES = "devices"
|
CONFIG_DEVICES = "devices"
|
||||||
CONFIG_PREFIX = "prefix"
|
CONFIG_PREFIX = "prefix"
|
||||||
|
@ -2,35 +2,53 @@ import logging
|
|||||||
|
|
||||||
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
||||||
|
|
||||||
from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, DATA_ADDERS, CONFIG_DEVICES, DATA_CONFIG, CONFIG_PREFIX, CONFIG_DISABLE, CONFIG_DISABLE_ALL
|
from .const import (
|
||||||
|
DOMAIN,
|
||||||
|
DATA_DEVICES,
|
||||||
|
DATA_ALIASES,
|
||||||
|
DATA_ADDERS,
|
||||||
|
CONFIG_DEVICES,
|
||||||
|
DATA_CONFIG,
|
||||||
|
CONFIG_PREFIX,
|
||||||
|
CONFIG_DISABLE,
|
||||||
|
CONFIG_DISABLE_ALL,
|
||||||
|
DATA_SETUP_COMPLETE,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_devices(hass):
|
def get_devices(hass):
|
||||||
return hass.data[DOMAIN][DATA_DEVICES]
|
return hass.data[DOMAIN][DATA_DEVICES]
|
||||||
|
|
||||||
|
|
||||||
def get_alias(hass, deviceID):
|
def get_alias(hass, deviceID):
|
||||||
for k,v in hass.data[DOMAIN][DATA_ALIASES].items():
|
for k, v in hass.data[DOMAIN][DATA_ALIASES].items():
|
||||||
if v == deviceID:
|
if v == deviceID:
|
||||||
return k
|
return k
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_config(hass, deviceID):
|
def get_config(hass, deviceID):
|
||||||
config = hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DEVICES, {})
|
config = hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DEVICES, {})
|
||||||
return config.get(deviceID, config.get(deviceID.replace('-','_'), {}))
|
return config.get(deviceID, config.get(deviceID.replace('-', '_'), {}))
|
||||||
|
|
||||||
|
|
||||||
def create_entity(hass, platform, deviceID, connection):
|
def create_entity(hass, platform, deviceID, connection):
|
||||||
conf = get_config(hass, deviceID)
|
conf = get_config(hass, deviceID)
|
||||||
if conf and (platform in conf.get(CONFIG_DISABLE, [])
|
if conf and (platform in conf.get(CONFIG_DISABLE, [])
|
||||||
or CONFIG_DISABLE_ALL in conf.get(CONFIG_DISABLE, [])):
|
or CONFIG_DISABLE_ALL in conf.get(CONFIG_DISABLE, [])):
|
||||||
return None
|
return None
|
||||||
if not conf and (platform in hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DISABLE, [])
|
if not conf and \
|
||||||
or CONFIG_DISABLE_ALL in hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DISABLE, [])):
|
(platform in hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DISABLE, [])
|
||||||
|
or CONFIG_DISABLE_ALL in
|
||||||
|
hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DISABLE, [])):
|
||||||
return None
|
return None
|
||||||
adder = hass.data[DOMAIN][DATA_ADDERS][platform]
|
adder = hass.data[DOMAIN][DATA_ADDERS][platform]
|
||||||
entity = adder(hass, deviceID, connection, get_alias(hass, deviceID))
|
entity = adder(hass, deviceID, connection, get_alias(hass, deviceID))
|
||||||
return entity
|
return entity
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, async_add_devices, platform, cls):
|
def setup_platform(hass, config, async_add_devices, platform, cls):
|
||||||
def adder(hass, deviceID, connection, alias=None):
|
def adder(hass, deviceID, connection, alias=None):
|
||||||
entity = cls(hass, connection, deviceID, alias)
|
entity = cls(hass, connection, deviceID, alias)
|
||||||
@ -39,6 +57,11 @@ def setup_platform(hass, config, async_add_devices, platform, cls):
|
|||||||
hass.data[DOMAIN][DATA_ADDERS][platform] = adder
|
hass.data[DOMAIN][DATA_ADDERS][platform] = adder
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_setup_complete(hass):
|
||||||
|
return hass.data[DOMAIN][DATA_SETUP_COMPLETE]
|
||||||
|
|
||||||
|
|
||||||
class BrowserModEntity(Entity):
|
class BrowserModEntity(Entity):
|
||||||
|
|
||||||
def __init__(self, hass, connection, deviceID, alias=None):
|
def __init__(self, hass, connection, deviceID, alias=None):
|
||||||
@ -47,7 +70,11 @@ class BrowserModEntity(Entity):
|
|||||||
self.deviceID = deviceID
|
self.deviceID = deviceID
|
||||||
self._data = {}
|
self._data = {}
|
||||||
prefix = hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_PREFIX, '')
|
prefix = hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_PREFIX, '')
|
||||||
self.entity_id = async_generate_entity_id(self.domain+".{}", alias or f"{prefix}{deviceID}", hass=hass)
|
self.entity_id = async_generate_entity_id(
|
||||||
|
self.domain+".{}",
|
||||||
|
alias or f"{prefix}{deviceID}",
|
||||||
|
hass=hass
|
||||||
|
)
|
||||||
|
|
||||||
def updated(self):
|
def updated(self):
|
||||||
pass
|
pass
|
||||||
@ -55,6 +82,7 @@ class BrowserModEntity(Entity):
|
|||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
@data.setter
|
@data.setter
|
||||||
def data(self, data):
|
def data(self, data):
|
||||||
self._data = data
|
self._data = data
|
||||||
|
@ -48,6 +48,10 @@ class BrowserModLight(LightEntity, BrowserModEntity):
|
|||||||
return SUPPORT_BRIGHTNESS
|
return SUPPORT_BRIGHTNESS
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def brightness(self):
|
||||||
|
return self.data.get('brightness', None)
|
||||||
|
|
||||||
def turn_on(self, **kwargs):
|
def turn_on(self, **kwargs):
|
||||||
self.connection.send("no-blackout", **kwargs)
|
self.connection.send("no-blackout", **kwargs)
|
||||||
|
|
||||||
|
@ -1,9 +1,85 @@
|
|||||||
command:
|
command:
|
||||||
description: Send a command to a browser
|
description: 'Send a command to a browser.'
|
||||||
fields:
|
fields:
|
||||||
command:
|
command:
|
||||||
description: Command to send
|
description: 'Command to send'
|
||||||
example: 'navigate'
|
example: 'navigate'
|
||||||
deviceID:
|
deviceID:
|
||||||
description: List of receiving browsers
|
description: 'List of receiving browsers'
|
||||||
example: '["99980b13-dabc9563", "office_computer"]'
|
example: '["99980b13-dabc9563", "office_computer"]'
|
||||||
|
debug:
|
||||||
|
description: 'On all browsers, show a popup, and a javascript alert with the current device ID.'
|
||||||
|
set_theme:
|
||||||
|
description: 'On all browsers, change the theme.'
|
||||||
|
fields:
|
||||||
|
theme:
|
||||||
|
description: 'Theme to change to'
|
||||||
|
example: '{theme: "clear_light"}'
|
||||||
|
navigate:
|
||||||
|
description: 'Navigate to a path on a browser.'
|
||||||
|
fields:
|
||||||
|
navigation_path:
|
||||||
|
description: 'Path to navigate to'
|
||||||
|
example: '/lovelace/1'
|
||||||
|
deviceID:
|
||||||
|
description: 'List of receiving browsers'
|
||||||
|
example: '["99980b13-dabc9563", "office_computer"]'
|
||||||
|
more_info:
|
||||||
|
description: 'Open the more info dialog of an entity on a browser.'
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: 'Entity to show more info for'
|
||||||
|
example: 'camera.front_door'
|
||||||
|
deviceID:
|
||||||
|
description: 'List of receiving browsers'
|
||||||
|
example: '["99980b13-dabc9563", "office_computer"]'
|
||||||
|
large:
|
||||||
|
description: '(optional) Set to true to make wider'
|
||||||
|
example: 'true'
|
||||||
|
toast:
|
||||||
|
description: 'Show a toast message in the bottom left on all browsers.'
|
||||||
|
fields:
|
||||||
|
message:
|
||||||
|
description: 'Message to show'
|
||||||
|
example: 'Short message'
|
||||||
|
duration:
|
||||||
|
description: '(optional) Time in milliseconds to show message for. Set to 0 for persistent display.'
|
||||||
|
example: '10000'
|
||||||
|
popup:
|
||||||
|
description: 'Pop up a card on a browser.'
|
||||||
|
fields:
|
||||||
|
title:
|
||||||
|
description: 'Name to show in popup bar'
|
||||||
|
example: 'Popup example'
|
||||||
|
card:
|
||||||
|
description: 'YAML config for card to show'
|
||||||
|
deviceID:
|
||||||
|
description: 'List of receiving browsers'
|
||||||
|
example: '["99980b13-dabc9563", "office_computer"]'
|
||||||
|
large:
|
||||||
|
description: '(optional) Set to true to make wider'
|
||||||
|
example: 'true'
|
||||||
|
hide_header:
|
||||||
|
description: '(optional) Hide header title and close button'
|
||||||
|
example: 'true'
|
||||||
|
auto_close:
|
||||||
|
description: '(optional) Close popup when mouse is moved or key is pressed. Also hides header'
|
||||||
|
example: 'true'
|
||||||
|
time:
|
||||||
|
description: "(optional) When mouse isn't moved or keys aren't pressed for this amount of seconds, reopen. Only usable with auto_close. See blackout"
|
||||||
|
example: '20'
|
||||||
|
close_popup:
|
||||||
|
description: 'Close all popups on all browsers.'
|
||||||
|
blackout:
|
||||||
|
description: 'Cover screen in black until the mouse is moved or a key is pressed.'
|
||||||
|
fields:
|
||||||
|
time:
|
||||||
|
description: '(optional) The blackout will turn on automatically after the specified number of seconds. It works kind of like a screensaver and will keep turning on until blackout is called again with time: -1.'
|
||||||
|
example: '20'
|
||||||
|
no_blackout:
|
||||||
|
description: 'Remove a blackout from a browser.'
|
||||||
|
fields:
|
||||||
|
brightness:
|
||||||
|
description: '(optional) On a Fully Kiosk Browser Plus set the screen brightness from 0 - 255.'
|
||||||
|
lovelace_reload:
|
||||||
|
description: 'Refresh the lovelace configuration.'
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { LitElement, html, css } from "card-tools/src/lit-element";
|
import { deviceID, setDeviceID } from "card-tools/src/deviceId"
|
||||||
import { deviceID } from "card-tools/src/deviceID"
|
|
||||||
import { moreInfo } from "card-tools/src/more-info"
|
import { moreInfo } from "card-tools/src/more-info"
|
||||||
import "./browser-player-editor.js"
|
import "./browser-player-editor.js"
|
||||||
|
|
||||||
@ -29,22 +28,31 @@ class BrowserPlayer extends LitElement {
|
|||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
for (const event of ["play", "pause", "ended", "volumechange", "canplay", "loadeddata"])
|
||||||
|
window.browser_mod.player.addEventListener(event, () => this.requestUpdate());
|
||||||
}
|
}
|
||||||
handleMute(ev) {
|
handleMute(ev) {
|
||||||
window.browser_mod.mute({});
|
window.browser_mod.player_mute();
|
||||||
}
|
}
|
||||||
handleVolumeChange(ev) {
|
handleVolumeChange(ev) {
|
||||||
const vol = parseFloat(ev.target.value);
|
const vol = parseFloat(ev.target.value);
|
||||||
window.browser_mod.set_volume({volume_level: vol});
|
window.browser_mod.player_set_volume(vol);
|
||||||
}
|
}
|
||||||
handleMoreInfo(ev) {
|
handleMoreInfo(ev) {
|
||||||
moreInfo("media_player."+window.browser_mod.entity_id);
|
moreInfo("media_player."+window.browser_mod.entity_id);
|
||||||
}
|
}
|
||||||
handlePlayPause(ev) {
|
handlePlayPause(ev) {
|
||||||
if (window.browser_mod.player.paused)
|
if (window.browser_mod.player.paused)
|
||||||
window.browser_mod.play({});
|
window.browser_mod.player_play();
|
||||||
else
|
else
|
||||||
window.browser_mod.pause({});
|
window.browser_mod.player_pause();
|
||||||
|
}
|
||||||
|
setDeviceID() {
|
||||||
|
const newID = prompt("Set deviceID", deviceID);
|
||||||
|
if (newID !== deviceID) {
|
||||||
|
setDeviceID(newID);
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -63,34 +71,34 @@ class BrowserPlayer extends LitElement {
|
|||||||
}
|
}
|
||||||
@click=${this.handleMute}
|
@click=${this.handleMute}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-paper-slider
|
<ha-slider
|
||||||
min=0
|
min=0
|
||||||
max=1
|
max=1
|
||||||
step=0.01
|
step=0.01
|
||||||
?disabled=${player.muted}
|
?disabled=${player.muted}
|
||||||
value=${player.volume}
|
value=${player.volume}
|
||||||
@change=${this.handleVolumeChange}
|
@change=${this.handleVolumeChange}
|
||||||
></ha-paper-slider>
|
></ha-slider>
|
||||||
|
|
||||||
${window.browser_mod.player_state === "stopped"
|
${window.browser_mod.player_state === "stopped"
|
||||||
? html`<div class="placeholder"></div>`
|
? html`<div class="placeholder"></div>`
|
||||||
: html`
|
: html`
|
||||||
<paper-icon-button
|
<ha-icon-button
|
||||||
.icon=${player.paused
|
.icon=${player.paused
|
||||||
? "mdi:play"
|
? "mdi:play"
|
||||||
: "mdi:pause"
|
: "mdi:pause"
|
||||||
}
|
}
|
||||||
@click=${this.handlePlayPause}
|
@click=${this.handlePlayPause}
|
||||||
highlight
|
highlight
|
||||||
></paper-icon-button>
|
></ha-icon-button>
|
||||||
`}
|
`}
|
||||||
<paper-icon-button
|
<ha-icon-button
|
||||||
.icon=${"mdi:settings"}
|
.icon=${"mdi:cog"}
|
||||||
@click=${this.handleMoreInfo}
|
@click=${this.handleMoreInfo}
|
||||||
></paper-icon-button>
|
></ha-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="device-id">
|
<div class="device-id" @click=${this.setDeviceID}>
|
||||||
${deviceID}
|
${deviceID}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
35
js/browser.js
Normal file
35
js/browser.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { fireEvent } from "card-tools/src/event";
|
||||||
|
|
||||||
|
export const BrowserModBrowserMixin = (C) => class extends C {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
document.addEventListener("visibilitychange", () => this.sensor_update());
|
||||||
|
window.addEventListener("location-changed", () => this.sensor_update());
|
||||||
|
|
||||||
|
window.setInterval(() => this.sensor_update(), 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
sensor_update() {
|
||||||
|
window.queueMicrotask( async () => {
|
||||||
|
const battery = navigator.getBattery ? await navigator.getBattery() : undefined;
|
||||||
|
this.sendUpdate({browser: {
|
||||||
|
path: window.location.pathname,
|
||||||
|
visibility: document.visibilityState,
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
currentUser: this._hass &&this._hass.user && this._hass.user.name,
|
||||||
|
fullyKiosk: this.isFully,
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
battery_level: this.isFully ? window.fully.getBatteryLevel() : battery ? battery.level*100 : undefined,
|
||||||
|
charging: this.isFully ? window.fully.isPlugged() : battery ? battery.charging : undefined,
|
||||||
|
}});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
do_navigate(path) {
|
||||||
|
if (!path) return;
|
||||||
|
history.pushState(null, "", path);
|
||||||
|
fireEvent("location-changed", {}, document.querySelector("home-assistant"));
|
||||||
|
}
|
||||||
|
}
|
46
js/camera.js
Normal file
46
js/camera.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
export const BrowserModCameraMixin = (C) => class extends C {
|
||||||
|
|
||||||
|
setup_camera() {
|
||||||
|
|
||||||
|
console.log("Starting camera")
|
||||||
|
|
||||||
|
if(this._video) return;
|
||||||
|
this._video = document.createElement("video");
|
||||||
|
this._video.autoplay = true;
|
||||||
|
this._video.playsInline = true;
|
||||||
|
this._video.style.display = "none";
|
||||||
|
|
||||||
|
this._canvas = document.createElement("canvas");
|
||||||
|
this._canvas.style.display = "none";
|
||||||
|
|
||||||
|
document.body.appendChild(this._video);
|
||||||
|
document.body.appendChild(this._canvas);
|
||||||
|
|
||||||
|
if(!navigator.mediaDevices) return;
|
||||||
|
|
||||||
|
console.log("Starting devices")
|
||||||
|
navigator.mediaDevices.getUserMedia({video: true, audio: false}).then((stream) => {
|
||||||
|
this._video.srcObject = stream;
|
||||||
|
this._video.play();
|
||||||
|
this.update_camera();
|
||||||
|
})
|
||||||
|
|
||||||
|
this._camera_framerate = 2;
|
||||||
|
|
||||||
|
window.addEventListener("click", () => this._video.play(), {once: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
update_camera() {
|
||||||
|
this._canvas.width = this._video.videoWidth;
|
||||||
|
this._canvas.height = this._video.videoHeight;
|
||||||
|
|
||||||
|
const context = this._canvas.getContext('2d');
|
||||||
|
context.drawImage(this._video, 0, 0, this._video.videoWidth, this._video.videoHeight);
|
||||||
|
|
||||||
|
this.sendUpdate({
|
||||||
|
camera: this._canvas.toDataURL('image/jpeg'),
|
||||||
|
});
|
||||||
|
setTimeout(() => this.update_camera(), Math.round(1000 / this._camera_framerate));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
71
js/connection.js
Normal file
71
js/connection.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { deviceID } from "card-tools/src/deviceId";
|
||||||
|
import { hass, provideHass } from "card-tools/src/hass";
|
||||||
|
|
||||||
|
export class BrowserModConnection{
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
const isCast = document.querySelector("hc-main") !== null;
|
||||||
|
if(!isCast) {
|
||||||
|
if(!window.hassConnection) {
|
||||||
|
window.setTimeout(() => this._do_connect(), 100);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this._connection = (await window.hassConnection).conn;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._connection = hass().connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._connection.subscribeMessage((msg) => this.msg_callback(msg), {
|
||||||
|
type: 'browser_mod/connect',
|
||||||
|
deviceID: deviceID,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._hass_patched = false;
|
||||||
|
provideHass(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
set hass(hass) {
|
||||||
|
this._hass = hass;
|
||||||
|
if(!hass || this._hass_patched) return;
|
||||||
|
|
||||||
|
this._hass_patched = true;
|
||||||
|
const callService = hass.callService;
|
||||||
|
|
||||||
|
hass.callService = (domain, service, serviceData) => {
|
||||||
|
if(serviceData && serviceData.deviceID) {
|
||||||
|
serviceData = JSON.parse(JSON.stringify(serviceData));
|
||||||
|
|
||||||
|
const orig = JSON.stringify(serviceData.deviceID);
|
||||||
|
const patched = orig.replace('"this"', `"${deviceID}"`);
|
||||||
|
serviceData.deviceID = JSON.parse(patched);
|
||||||
|
}
|
||||||
|
return callService(domain, service, serviceData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.querySelector("hc-main"))
|
||||||
|
document.querySelector("hc-main").hassChanged(hass, hass);
|
||||||
|
else
|
||||||
|
document.querySelector("home-assistant").hassChanged(hass, hass);
|
||||||
|
}
|
||||||
|
|
||||||
|
get connected() {
|
||||||
|
return this._connection !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_callback(message) {
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendUpdate(data) {
|
||||||
|
if(!this.connected) return;
|
||||||
|
this._connection.sendMessage({
|
||||||
|
type: 'browser_mod/update',
|
||||||
|
deviceID,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
39
js/fullyKiosk.js
Normal file
39
js/fullyKiosk.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
export const FullyKioskMixin = (C) => class extends C {
|
||||||
|
get isFully() {
|
||||||
|
return window.fully !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (!this.isFully) return;
|
||||||
|
|
||||||
|
this._fullyMotion = false;
|
||||||
|
this._motionTimeout = undefined;
|
||||||
|
|
||||||
|
for (const event of ["screenOn", "screenOff", "pluggedAC", "pluggedUSB", "onBatteryLevelChanged", "unplugged", "networkReconnect"]) {
|
||||||
|
fully.bind(event, "window.browser_mod.fully_update();");
|
||||||
|
}
|
||||||
|
|
||||||
|
window.fully.bind("onMotion", "window.browser_mod.fullyMotionTriggered();");
|
||||||
|
}
|
||||||
|
|
||||||
|
fully_update() {
|
||||||
|
if(!this.isFully) return
|
||||||
|
this.sendUpdate({fully: {
|
||||||
|
battery: window.fully.getBatteryLevel(),
|
||||||
|
charging: window.fully.isPlugged(),
|
||||||
|
motion: this._fullyMotion,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
fullyMotionTriggered() {
|
||||||
|
this._fullyMotion = true;
|
||||||
|
clearTimeout(this._motionTimeout);
|
||||||
|
this._motionTimeout = setTimeout(() => {
|
||||||
|
this._fullyMotion = false;
|
||||||
|
this.fully_update();
|
||||||
|
}, 5000);
|
||||||
|
this.fully_update();
|
||||||
|
}
|
||||||
|
}
|
455
js/main.js
455
js/main.js
@ -1,123 +1,36 @@
|
|||||||
import { deviceID } from "card-tools/src/deviceID";
|
import { deviceID } from "card-tools/src/deviceID";
|
||||||
import { lovelace_view, provideHass, load_lovelace, lovelace, hass } from "card-tools/src/hass";
|
import { lovelace_view } from "card-tools/src/hass";
|
||||||
import { popUp, closePopUp } from "card-tools/src/popup";
|
import { popUp } from "card-tools/src/popup";
|
||||||
import { fireEvent } from "card-tools/src/event";
|
import { fireEvent } from "card-tools/src/event";
|
||||||
import { moreInfo } from "card-tools/src/more-info.js";
|
|
||||||
import "./browser-player";
|
import "./browser-player";
|
||||||
|
|
||||||
class BrowserMod {
|
import { BrowserModConnection } from "./connection";
|
||||||
|
import { BrowserModMediaPlayerMixin } from "./mediaPlayer";
|
||||||
|
import { FullyKioskMixin } from "./fullyKiosk";
|
||||||
|
import { BrowserModCameraMixin } from "./camera";
|
||||||
|
import { BrowserModScreensaverMixin } from "./screensaver";
|
||||||
|
import { BrowserModPopupsMixin } from "./popups";
|
||||||
|
import { BrowserModBrowserMixin } from "./browser";
|
||||||
|
|
||||||
set hass(hass) {
|
|
||||||
if(!hass) return;
|
|
||||||
this._hass = hass;
|
|
||||||
if(this.hassPatched) return;
|
|
||||||
const callService = hass.callService;
|
|
||||||
const newCallService = (domain, service, serviceData) => {
|
|
||||||
if(serviceData && serviceData.deviceID) {
|
|
||||||
if(Array.isArray(serviceData.deviceID)) {
|
|
||||||
const index = serviceData.deviceID.indexOf('this');
|
|
||||||
if(index !== -1) {
|
|
||||||
serviceData = JSON.parse(JSON.stringify(serviceData));
|
|
||||||
serviceData.deviceID[index] = deviceID;
|
|
||||||
}
|
|
||||||
} else if(serviceData.deviceID === "this") {
|
|
||||||
serviceData = JSON.parse(JSON.stringify(serviceData));
|
|
||||||
serviceData.deviceID = deviceID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return callService(domain, service, serviceData);
|
|
||||||
};
|
|
||||||
hass.callService = newCallService;
|
|
||||||
|
|
||||||
this.hassPatched = true;
|
const ext = (baseClass, mixins) =>
|
||||||
if(document.querySelector("hc-main"))
|
mixins.reduceRight((base, mixin) => mixin(base), baseClass);
|
||||||
document.querySelector("hc-main").hassChanged(hass,hass);
|
|
||||||
else
|
|
||||||
document.querySelector("home-assistant").hassChanged(hass, hass);
|
|
||||||
}
|
|
||||||
|
|
||||||
playOnce(ev) {
|
class BrowserMod extends ext(BrowserModConnection, [
|
||||||
if(this._video) this._video.play();
|
BrowserModBrowserMixin,
|
||||||
if(window.browser_mod.playedOnce) return;
|
BrowserModPopupsMixin,
|
||||||
window.browser_mod.player.play();
|
BrowserModScreensaverMixin,
|
||||||
window.browser_mod.playedOnce = true;
|
BrowserModCameraMixin,
|
||||||
}
|
FullyKioskMixin,
|
||||||
|
BrowserModMediaPlayerMixin,
|
||||||
|
]) {
|
||||||
|
|
||||||
async _load_lovelace() {
|
|
||||||
if(!await load_lovelace()) {
|
|
||||||
let timer = window.setTimeout(this._load_lovelace.bind(this), 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_connect() {
|
|
||||||
if(!window.hassConnection) {
|
|
||||||
window.setTimeout(() => this._connect(), 100);
|
|
||||||
} else {
|
|
||||||
window.hassConnection.then((conn) => this.connect(conn.conn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.entity_id = deviceID.replace("-","_");
|
super();
|
||||||
|
this.entity_id = deviceID.replace("-", "_");
|
||||||
this.cast = document.querySelector("hc-main") !== null;
|
this.cast = document.querySelector("hc-main") !== null;
|
||||||
if(!this.cast) {
|
this.connect();
|
||||||
window.setTimeout(this._load_lovelace.bind(this), 500);
|
|
||||||
this._connect();
|
|
||||||
document.querySelector("home-assistant").addEventListener("hass-more-info", this.popup_card.bind(this));
|
|
||||||
} else {
|
|
||||||
this.connect(hass().connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.player = new Audio();
|
|
||||||
this.playedOnce = false;
|
|
||||||
|
|
||||||
this.autoclose_popup_active = false;
|
|
||||||
|
|
||||||
const updater = this.update.bind(this);
|
|
||||||
this.player.addEventListener("ended", updater);
|
|
||||||
this.player.addEventListener("play", updater);
|
|
||||||
this.player.addEventListener("pause", updater);
|
|
||||||
this.player.addEventListener("volumechange", updater);
|
|
||||||
document.addEventListener("visibilitychange", updater);
|
|
||||||
window.addEventListener("location-changed", updater);
|
|
||||||
window.addEventListener("click", this.playOnce);
|
|
||||||
window.addEventListener("mousemove", this.no_blackout.bind(this));
|
|
||||||
window.addEventListener("mousedown", this.no_blackout.bind(this));
|
|
||||||
window.addEventListener("keydown", this.no_blackout.bind(this));
|
|
||||||
window.addEventListener("touchstart", this.no_blackout.bind(this));
|
|
||||||
provideHass(this);
|
|
||||||
|
|
||||||
if(window.fully)
|
|
||||||
{
|
|
||||||
this._fullyMotion = false;
|
|
||||||
this._motionTimeout = undefined;
|
|
||||||
fully.bind('screenOn', 'browser_mod.update();');
|
|
||||||
fully.bind('screenOff', 'browser_mod.update();');
|
|
||||||
fully.bind('pluggedAC', 'browser_mod.update();');
|
|
||||||
fully.bind('pluggedUSB', 'browser_mod.update();');
|
|
||||||
fully.bind('onBatteryLevelChanged', 'browser_mod.update();');
|
|
||||||
fully.bind('unplugged', 'browser_mod.update();');
|
|
||||||
fully.bind('networkReconnect', 'browser_mod.update();');
|
|
||||||
|
|
||||||
fully.bind('onMotion', 'browser_mod.fullyMotion();');
|
|
||||||
}
|
|
||||||
|
|
||||||
this._screenSaver = undefined;
|
|
||||||
this._screenSaverTimer = undefined;
|
|
||||||
this._screenSaverTime = 0;
|
|
||||||
this._blackout = document.createElement("div");
|
|
||||||
this._blackout.style.cssText = `
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: black;
|
|
||||||
visibility: hidden;
|
|
||||||
`;
|
|
||||||
document.body.appendChild(this._blackout);
|
|
||||||
|
|
||||||
const pjson = require('../package.json');
|
const pjson = require('../package.json');
|
||||||
console.info(`%cBROWSER_MOD ${pjson.version} IS INSTALLED
|
console.info(`%cBROWSER_MOD ${pjson.version} IS INSTALLED
|
||||||
@ -125,96 +38,37 @@ class BrowserMod {
|
|||||||
"color: green; font-weight: bold", "");
|
"color: green; font-weight: bold", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(conn) {
|
msg_callback(msg) {
|
||||||
this.conn = conn
|
const handlers = {
|
||||||
conn.subscribeMessage((msg) => this.callback(msg), {
|
update: (msg) => this.update(msg),
|
||||||
type: 'browser_mod/connect',
|
debug: (msg) => this.debug(msg),
|
||||||
deviceID: deviceID,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(msg) {
|
play: (msg) => this.player_play(msg.media_content_id),
|
||||||
switch (msg.command) {
|
pause: (msg) => this.player_pause(),
|
||||||
case "update":
|
stop: (msg) => this.player_stop(),
|
||||||
this.update(msg);
|
set_volume: (msg) => this.player_set_volume(msg.volume_level),
|
||||||
break;
|
mute: (msg) => this.player_mute(msg.mute),
|
||||||
|
|
||||||
case "debug":
|
toast: (msg) => this.do_toast(msg.message, msg.duration),
|
||||||
this.debug(msg);
|
popup: (msg) => this.do_popup(msg),
|
||||||
break;
|
"close-popup": (msg) => this.do_close_popup(),
|
||||||
|
"more-info": (msg) => this.do_more_info(msg.entity_id, msg.large),
|
||||||
|
|
||||||
case "play":
|
navigate: (msg) => this.do_navigate(msg.navigation_path),
|
||||||
this.play(msg);
|
"set-theme": (msg) => this.set_theme(msg),
|
||||||
break;
|
"lovelace-reload": (msg) => this.lovelace_reload(msg),
|
||||||
case "pause":
|
"window-reload": () => window.location.reload(false),
|
||||||
this.pause(msg);
|
|
||||||
break;
|
|
||||||
case "stop":
|
|
||||||
this.stop(msg);
|
|
||||||
break;
|
|
||||||
case "set_volume":
|
|
||||||
this.set_volume(msg);
|
|
||||||
break;
|
|
||||||
case "mute":
|
|
||||||
this.mute(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "toast":
|
blackout: (msg) => this.do_blackout(msg.time ? parseInt(msg.time) : undefined),
|
||||||
this.toast(msg);
|
"no-blackout": (msg) => {
|
||||||
break;
|
if(msg.brightness && this.isFully) {
|
||||||
case "popup":
|
window.fully.setScreenBrightness(msg.brightness);
|
||||||
this.popup(msg);
|
}
|
||||||
break;
|
this.no_blackout()
|
||||||
case "close-popup":
|
},
|
||||||
this.close_popup(msg);
|
|
||||||
break;
|
|
||||||
case "navigate":
|
|
||||||
this.navigate(msg);
|
|
||||||
break;
|
|
||||||
case "more-info":
|
|
||||||
this.more_info(msg);
|
|
||||||
break;
|
|
||||||
case "set-theme":
|
|
||||||
this.set_theme(msg);
|
|
||||||
break;
|
|
||||||
case "lovelace-reload":
|
|
||||||
this.lovelace_reload(msg);
|
|
||||||
break;
|
|
||||||
case "window-reload":
|
|
||||||
window.location.reload(false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "blackout":
|
|
||||||
this.blackout(msg);
|
|
||||||
break;
|
|
||||||
case "no-blackout":
|
|
||||||
this.no_blackout(msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get player_state() {
|
|
||||||
if (!this.player.src) return "stopped";
|
|
||||||
if (this.player.ended) return "stopped";
|
|
||||||
if (this.player.paused) return "paused";
|
|
||||||
return "playing";
|
|
||||||
}
|
|
||||||
|
|
||||||
popup_card(ev) {
|
|
||||||
if(!lovelace()) return;
|
|
||||||
const ll = lovelace();
|
|
||||||
const data = {
|
|
||||||
...ll.config.popup_cards,
|
|
||||||
...ll.config.views[ll.current_view].popup_cards,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!ev.detail || !ev.detail.entityId) return;
|
handlers[msg.command](msg);
|
||||||
const d = data[ev.detail.entityId];
|
|
||||||
if(!d) return;
|
|
||||||
window.setTimeout(() => {
|
|
||||||
fireEvent("hass-more-info", {entityId: "."}, document.querySelector("home-assistant"));
|
|
||||||
popUp(d.title, d.card, d.large || false, d.style);
|
|
||||||
}, 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(msg) {
|
debug(msg) {
|
||||||
@ -222,90 +76,9 @@ class BrowserMod {
|
|||||||
alert(deviceID);
|
alert(deviceID);
|
||||||
}
|
}
|
||||||
|
|
||||||
_set_screensaver(fn, time) {
|
|
||||||
clearTimeout(this._screenSaverTimer);
|
|
||||||
if(!fn) {
|
|
||||||
if(this._screenSaverTime)
|
|
||||||
this._screenSaverTimer = setTimeout(this._screenSaver, this._screenSaverTime)
|
|
||||||
} else {
|
|
||||||
time = parseInt(time)
|
|
||||||
if(time == -1) {
|
|
||||||
clearTimeout(this._screenSaverTimer);
|
|
||||||
this._screenSaverTime = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._screenSaverTime = time * 1000;
|
|
||||||
this._screenSaver = fn;
|
|
||||||
this._screenSaverTimer = setTimeout(this._screenSaver, this._screenSaverTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
play(msg) {
|
|
||||||
const src = msg.media_content_id;
|
|
||||||
if(src)
|
|
||||||
this.player.src = src;
|
|
||||||
this.player.play();
|
|
||||||
}
|
|
||||||
pause(msg) {
|
|
||||||
this.player.pause();
|
|
||||||
}
|
|
||||||
stop(msg) {
|
|
||||||
this.player.pause();
|
|
||||||
this.player.src = null;
|
|
||||||
}
|
|
||||||
set_volume(msg) {
|
|
||||||
if (msg.volume_level === undefined) return;
|
|
||||||
this.player.volume = msg.volume_level;
|
|
||||||
}
|
|
||||||
mute(msg) {
|
|
||||||
if (msg.mute === undefined)
|
|
||||||
msg.mute = !this.player.muted;
|
|
||||||
this.player.muted = Boolean(msg.mute)
|
|
||||||
}
|
|
||||||
|
|
||||||
toast(msg) {
|
|
||||||
if(!msg.message) return;
|
|
||||||
|
|
||||||
fireEvent("hass-notification", {
|
|
||||||
message: msg.message,
|
|
||||||
duration: msg.duration !== undefined ? parseInt(msg.duration) : undefined
|
|
||||||
}, document.querySelector("home-assistant"));
|
|
||||||
}
|
|
||||||
|
|
||||||
popup(msg){
|
|
||||||
if(!msg.title && !msg.auto_close) return;
|
|
||||||
if(!msg.card) return;
|
|
||||||
|
|
||||||
const fn = () => {
|
|
||||||
popUp(msg.title, msg.card, msg.large, msg.style, msg.auto_close||msg.hide_header);
|
|
||||||
if(msg.auto_close)
|
|
||||||
this.autoclose_popup_active = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
if(msg.auto_close && msg.time) {
|
|
||||||
this._set_screensaver(fn, msg.time);
|
|
||||||
} else {
|
|
||||||
// closePopUp();
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close_popup(msg){
|
|
||||||
this._set_screensaver();
|
|
||||||
this.autoclose_popup_active = false;
|
|
||||||
closePopUp();
|
|
||||||
}
|
|
||||||
navigate(msg){
|
|
||||||
if(!msg.navigation_path) return;
|
|
||||||
history.pushState(null, "", msg.navigation_path);
|
|
||||||
fireEvent("location-changed", {}, document.querySelector("home-assistant"));
|
|
||||||
}
|
|
||||||
more_info(msg){
|
|
||||||
if(!msg.entity_id) return;
|
|
||||||
moreInfo(msg.entity_id, msg.large);
|
|
||||||
}
|
|
||||||
set_theme(msg){
|
set_theme(msg){
|
||||||
if(!msg.theme) msg.theme = "default";
|
if(!msg.theme) msg.theme = "default";
|
||||||
fireEvent("settheme", msg.theme, document.querySelector("home-assistant"));
|
fireEvent("settheme", {theme: msg.theme}, document.querySelector("home-assistant"));
|
||||||
}
|
}
|
||||||
|
|
||||||
lovelace_reload(msg) {
|
lovelace_reload(msg) {
|
||||||
@ -314,145 +87,25 @@ class BrowserMod {
|
|||||||
fireEvent("config-refresh", {}, ll);
|
fireEvent("config-refresh", {}, ll);
|
||||||
}
|
}
|
||||||
|
|
||||||
blackout(msg){
|
|
||||||
const fn = () => {
|
|
||||||
if (window.fully)
|
|
||||||
{
|
|
||||||
fully.turnScreenOff();
|
|
||||||
} else {
|
|
||||||
this._blackout.style.visibility = "visible";
|
|
||||||
}
|
|
||||||
this.update();
|
|
||||||
};
|
|
||||||
if(msg.time) {
|
|
||||||
this._set_screensaver(fn, msg.time)
|
|
||||||
} else {
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
no_blackout(msg){
|
|
||||||
this._set_screensaver();
|
|
||||||
if(this.autoclose_popup_active)
|
|
||||||
return this.close_popup();
|
|
||||||
if (window.fully)
|
|
||||||
{
|
|
||||||
if (!fully.getScreenOn())
|
|
||||||
fully.turnScreenOn();
|
|
||||||
if (msg.brightness)
|
|
||||||
fully.setScreenBrightness(msg.brightness);
|
|
||||||
this.update();
|
|
||||||
} else {
|
|
||||||
if(this._blackout.style.visibility !== "hidden") {
|
|
||||||
this._blackout.style.visibility = "hidden";
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is_blackout(){
|
|
||||||
if (window.fully)
|
|
||||||
return !fully.getScreenOn();
|
|
||||||
return Boolean(this._blackout.style.visibility === "visible")
|
|
||||||
}
|
|
||||||
|
|
||||||
fullyMotion() {
|
|
||||||
this._fullyMotion = true;
|
|
||||||
clearTimeout(this._motionTimeout);
|
|
||||||
this._motionTimeout = setTimeout(() => {
|
|
||||||
this._fullyMotion = false;
|
|
||||||
this.update();
|
|
||||||
}, 5000);
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
start_camera() {
|
|
||||||
if(this._video) return;
|
|
||||||
this._video = document.createElement("video");
|
|
||||||
this._video.autoplay = true;
|
|
||||||
this._video.playsInline = true;
|
|
||||||
this._video.style.cssText = `
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
`;
|
|
||||||
this._canvas = document.createElement("canvas");
|
|
||||||
this._canvas.style.cssText = `
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
`;
|
|
||||||
document.body.appendChild(this._canvas);
|
|
||||||
document.body.appendChild(this._video);
|
|
||||||
navigator.mediaDevices.getUserMedia({video: true, audio: false}).then((stream) => {
|
|
||||||
this._video.srcObject = stream;
|
|
||||||
this._video.play();
|
|
||||||
this.send_cam();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
send_cam(data) {
|
|
||||||
const context = this._canvas.getContext('2d');
|
|
||||||
context.drawImage(this._video, 0, 0, this._canvas.width, this._canvas.height);
|
|
||||||
this.conn.sendMessage({
|
|
||||||
type: 'browser_mod/update',
|
|
||||||
deviceID: deviceID,
|
|
||||||
data: {
|
|
||||||
camera: this._canvas.toDataURL('image/png'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
setTimeout(this.send_cam.bind(this), 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
update(msg=null) {
|
update(msg=null) {
|
||||||
if(!this.conn) return;
|
|
||||||
|
|
||||||
if(msg) {
|
if(msg) {
|
||||||
if(msg.name) {
|
if(msg.name) {
|
||||||
this.entity_id = msg.name.toLowerCase();
|
this.entity_id = msg.name.toLowerCase();
|
||||||
}
|
}
|
||||||
if(msg.camera) {
|
if(msg.camera) {
|
||||||
this.start_camera();
|
this.setup_camera();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.player_update();
|
||||||
this.conn.sendMessage({
|
this.fully_update();
|
||||||
type: 'browser_mod/update',
|
this.screen_update();
|
||||||
deviceID: deviceID,
|
this.sensor_update();
|
||||||
data: {
|
|
||||||
browser: {
|
|
||||||
path: window.location.pathname,
|
|
||||||
visibility: document.visibilityState,
|
|
||||||
userAgent: navigator.userAgent,
|
|
||||||
currentUser: this._hass && this._hass.user && this._hass.user.name,
|
|
||||||
fullyKiosk: window.fully ? true : undefined,
|
|
||||||
width: window.innerWidth,
|
|
||||||
height: window.innerHeight,
|
|
||||||
},
|
|
||||||
player: {
|
|
||||||
volume: this.player.volume,
|
|
||||||
muted: this.player.muted,
|
|
||||||
src: this.player.src,
|
|
||||||
state: this.player_state,
|
|
||||||
},
|
|
||||||
screen: {
|
|
||||||
blackout: this.is_blackout(),
|
|
||||||
brightness: window.fully ? fully.getScreenBrightness() : undefined,
|
|
||||||
},
|
|
||||||
fully: window.fully ? {
|
|
||||||
battery: window.fully ? fully.getBatteryLevel() : undefined,
|
|
||||||
charging: window.fully ? fully.isPlugged(): undefined,
|
|
||||||
motion: window.fully ? this._fullyMotion : undefined,
|
|
||||||
} : undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const bases = [customElements.whenDefined('home-assistant-main'), customElements.whenDefined('hui-view')];
|
const bases = [customElements.whenDefined('home-assistant'), customElements.whenDefined('hc-main')];
|
||||||
Promise.race(bases).then(() => {
|
Promise.race(bases).then(() => {
|
||||||
window.browser_mod = window.browser_mod || new BrowserMod();
|
window.browser_mod = window.browser_mod || new BrowserMod();
|
||||||
});
|
});
|
||||||
|
51
js/mediaPlayer.js
Normal file
51
js/mediaPlayer.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
export const BrowserModMediaPlayerMixin = (C) => class extends C {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.player = new Audio();
|
||||||
|
|
||||||
|
for (const event of ["play", "pause", "ended", "volumechange"]) {
|
||||||
|
this.player.addEventListener(event, () => this.player_update());
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("click", () => this.player.play(), {once: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
player_update(ev) {
|
||||||
|
this.sendUpdate({player: {
|
||||||
|
volume: this.player.volume,
|
||||||
|
muted: this.player.muted,
|
||||||
|
src: this.player.src,
|
||||||
|
state: this.player_state,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
get player_state() {
|
||||||
|
if (!this.player.src) return "stopped";
|
||||||
|
if (this.player.ended) return "stopped";
|
||||||
|
if (this.player.paused) return "paused";
|
||||||
|
return "playing";
|
||||||
|
}
|
||||||
|
|
||||||
|
player_play(src) {
|
||||||
|
if(src)
|
||||||
|
this.player.src = src;
|
||||||
|
this.player.play();
|
||||||
|
}
|
||||||
|
player_pause() {
|
||||||
|
this.player.pause();
|
||||||
|
}
|
||||||
|
player_stop() {
|
||||||
|
this.player.pause();
|
||||||
|
this.player.src = null;
|
||||||
|
}
|
||||||
|
player_set_volume(level) {
|
||||||
|
if(level === undefined) return;
|
||||||
|
this.player.volume = level;
|
||||||
|
}
|
||||||
|
player_mute(mute) {
|
||||||
|
if(mute === undefined)
|
||||||
|
mute = !this.player.muted;
|
||||||
|
this.player.muted = Boolean(mute);
|
||||||
|
}
|
||||||
|
}
|
79
js/popups.js
Normal file
79
js/popups.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { fireEvent } from "card-tools/src/event";
|
||||||
|
import { load_lovelace, lovelace } from "card-tools/src/hass";
|
||||||
|
import { moreInfo } from "card-tools/src/more-info";
|
||||||
|
import { closePopUp, popUp } from "card-tools/src/popup";
|
||||||
|
|
||||||
|
export const BrowserModPopupsMixin = (C) => class extends C {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
if (document.querySelector("home-assistant"))
|
||||||
|
document.querySelector("home-assistant").addEventListener("hass-more-info", (ev) => this._popup_card(ev));
|
||||||
|
|
||||||
|
const isCast = document.querySelector("hc-main") !== null;
|
||||||
|
if(!isCast)
|
||||||
|
load_lovelace();
|
||||||
|
}
|
||||||
|
|
||||||
|
_popup_card(ev) {
|
||||||
|
if(!lovelace()) return;
|
||||||
|
if(!ev.detail || !ev.detail.entityId) return;
|
||||||
|
const data = {
|
||||||
|
...lovelace().config.popup_cards,
|
||||||
|
...lovelace().config.views[lovelace().current_view].popup_cards,
|
||||||
|
};
|
||||||
|
const d = data[ev.detail.entityId];
|
||||||
|
if(!d) return;
|
||||||
|
|
||||||
|
window.queueMicrotask(() => {
|
||||||
|
fireEvent("hass-more-info", {entityID: "."}, document.querySelector("home-assistant"));
|
||||||
|
popUp(
|
||||||
|
d.title,
|
||||||
|
d.card,
|
||||||
|
d.large || false,
|
||||||
|
d.style
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
do_popup(cfg) {
|
||||||
|
if (!(cfg.title || cfg.auto_close || cfg.hide_header)) return;
|
||||||
|
if (!cfg.card) return;
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
popUp(
|
||||||
|
cfg.tile,
|
||||||
|
cfg.card,
|
||||||
|
cfg.large,
|
||||||
|
cfg.style,
|
||||||
|
cfg.auto_close || cfg.hide_header,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(cfg.auto_close) {
|
||||||
|
this.screensaver_set(open, closePopUp, cfg.time);
|
||||||
|
} else {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_close_popup() {
|
||||||
|
this.screensaver_stop();
|
||||||
|
closePopUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
do_more_info(entity_id, large) {
|
||||||
|
if (!entity_id) return;
|
||||||
|
moreInfo(entity_id, large);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_toast(message, duration) {
|
||||||
|
if (!message) return;
|
||||||
|
fireEvent("hass-notification", {
|
||||||
|
message,
|
||||||
|
duration: parseInt(duration),
|
||||||
|
}, document.querySelector("home-assistant"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
123
js/screensaver.js
Normal file
123
js/screensaver.js
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
export const BrowserModScreensaverMixin = (C) => class extends C {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._blackout_panel = document.createElement("div");
|
||||||
|
|
||||||
|
this._screenSaver = undefined;
|
||||||
|
this._screenSaverTimer = undefined;
|
||||||
|
this._screenSaverTimeOut = 0;
|
||||||
|
|
||||||
|
this._screenSaver = {
|
||||||
|
fn: undefined,
|
||||||
|
clearfn: undefined,
|
||||||
|
timer: undefined,
|
||||||
|
timeout: undefined,
|
||||||
|
listeners : {},
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this._blackout_panel.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: black;
|
||||||
|
display: none;
|
||||||
|
`;
|
||||||
|
document.body.appendChild(this._blackout_panel);
|
||||||
|
|
||||||
|
if(this.isFully) {
|
||||||
|
window.fully.bind("screenOn", "window.browser_mod.screen_update();");
|
||||||
|
window.fully.bind("screenOff", "window.browser_mod.screen_update();");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
screensaver_set(fn, clearfn, time) {
|
||||||
|
this._ss_clear();
|
||||||
|
this._screenSaver = {
|
||||||
|
fn,
|
||||||
|
clearfn,
|
||||||
|
timer: undefined,
|
||||||
|
timeout: time,
|
||||||
|
listeners: {},
|
||||||
|
active: false,
|
||||||
|
}
|
||||||
|
const l = () => this.screensaver_update();
|
||||||
|
for(const event of ["mousemove", "mousedown", "keydown", "touchstart"]) {
|
||||||
|
window.addEventListener(event, l);
|
||||||
|
this._screenSaver.listeners[event] = l;
|
||||||
|
}
|
||||||
|
this._screenSaver.timer = window.setTimeout(() => this._ss_run(), time*1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
screensaver_update() {
|
||||||
|
if (this._screenSaver.active) {
|
||||||
|
this.screensaver_stop();
|
||||||
|
} else {
|
||||||
|
window.clearTimeout(this._screenSaver.timer);
|
||||||
|
this._screenSaver.timer = window.setTimeout(() => this._ss_run(), this._screenSaver.timeout*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
screensaver_stop() {
|
||||||
|
this._ss_clear();
|
||||||
|
this._screenSaver.active = false;
|
||||||
|
if(this._screenSaver.clearfn)
|
||||||
|
this._screenSaver.clearfn();
|
||||||
|
if(this._screenSaver.timeout) {
|
||||||
|
this.screensaver_set(
|
||||||
|
this._screenSaver.fn,
|
||||||
|
this._screenSaver.clearfn,
|
||||||
|
this._screenSaver.timeout,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ss_clear() {
|
||||||
|
window.clearTimeout(this._screenSaverTimer);
|
||||||
|
for(const [k, v] of Object.entries(this._screenSaver.listeners)) {
|
||||||
|
window.removeEventListener(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ss_run() {
|
||||||
|
this._screenSaver.active = true;
|
||||||
|
this._screenSaver.fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
do_blackout(timeout) {
|
||||||
|
this.screensaver_set(
|
||||||
|
() => {
|
||||||
|
if(this.isFully)
|
||||||
|
window.fully.turnScreenOff();
|
||||||
|
else
|
||||||
|
this._blackout_panel.style.display = "block";
|
||||||
|
this.screen_update();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if(this._blackout_panel.style.display = "block")
|
||||||
|
this._blackout_panel.style.display = "none"
|
||||||
|
if(this.isFully && !window.fully.getScreenOn())
|
||||||
|
window.fully.turnScreenOn();
|
||||||
|
this.screen_update();
|
||||||
|
},
|
||||||
|
timeout || 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
no_blackout() {
|
||||||
|
this.screensaver_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
screen_update() {
|
||||||
|
this.sendUpdate({screen: {
|
||||||
|
blackout: this.isFully
|
||||||
|
? !window.fully.getScreenOn()
|
||||||
|
: Boolean(this._blackout_panel.style.display === "block"),
|
||||||
|
brightness: this.isFully ? window.fully.getScreenBrightness() : undefined,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
}
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "browser_mod",
|
"name": "browser_mod",
|
||||||
"version": "1.1.6",
|
"version": "1.2.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -656,7 +656,7 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"card-tools": {
|
"card-tools": {
|
||||||
"version": "github:thomasloven/lovelace-card-tools#6d5ae3800e4937aa424edc17108f43b964aecce7",
|
"version": "github:thomasloven/lovelace-card-tools#1272cf67c56e8f576e24c13f510568d544ad5d0b",
|
||||||
"from": "github:thomasloven/lovelace-card-tools"
|
"from": "github:thomasloven/lovelace-card-tools"
|
||||||
},
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "browser_mod",
|
"name": "browser_mod",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.1.6",
|
"version": "1.2.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack",
|
"build": "webpack",
|
||||||
|
@ -3,6 +3,12 @@ default_config:
|
|||||||
demo:
|
demo:
|
||||||
|
|
||||||
browser_mod:
|
browser_mod:
|
||||||
|
devices:
|
||||||
|
camdevice:
|
||||||
|
camera: true
|
||||||
|
testdevice:
|
||||||
|
alias: test
|
||||||
|
|
||||||
|
|
||||||
lovelace:
|
lovelace:
|
||||||
mode: yaml
|
mode: yaml
|
||||||
@ -17,6 +23,8 @@ lovelace:
|
|||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
themes:
|
themes:
|
||||||
|
red:
|
||||||
|
primary-color: red
|
||||||
test:
|
test:
|
||||||
card-mod-theme: test
|
card-mod-theme: test
|
||||||
card-mod-more-info-yaml: |
|
card-mod-more-info-yaml: |
|
||||||
@ -24,3 +32,12 @@ frontend:
|
|||||||
.mdc-dialog {
|
.mdc-dialog {
|
||||||
backdrop-filter: grayscale(0.7) blur(5px);
|
backdrop-filter: grayscale(0.7) blur(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tts:
|
||||||
|
- platform: google_translate
|
||||||
|
base_url: !env_var OUT_ADDR
|
||||||
|
|
||||||
|
script:
|
||||||
|
cm_debug:
|
||||||
|
sequence:
|
||||||
|
- service: browser_mod.debug
|
||||||
|
@ -52,6 +52,8 @@ services:
|
|||||||
volumes: *x-files
|
volumes: *x-files
|
||||||
ports:
|
ports:
|
||||||
- "5001:8123"
|
- "5001:8123"
|
||||||
|
environment:
|
||||||
|
OUT_ADDR: "http://${DOCKER_GATEWAY_HOST:-localhost}:5001"
|
||||||
command: *x-command
|
command: *x-command
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user