Add experimental camera support
This commit is contained in:
parent
a96667fb86
commit
f13ac064ca
25
README.md
25
README.md
@ -3,7 +3,7 @@ browser\_mod
|
||||
|
||||
[](https://github.com/custom-components/hacs)
|
||||
|
||||
A Home Assistant integration to turn your browser into a controllable entity - and also an audio player.
|
||||
A Home Assistant integration to turn your browser into a controllable entity - and also an audio player and a security camera (WIP).
|
||||
|
||||
## Example uses
|
||||
|
||||
@ -61,12 +61,13 @@ This binds the *aliases* `arrakis` to `99980b13-dabc9563` and `dashboard` to `d2
|
||||
Note: Aliases must be unique.
|
||||
|
||||
## Entities
|
||||
Once `browser_mod` is installed, loading up your Home Assistant frontend on a new *device* will create three or four new devices.
|
||||
Once `browser_mod` is installed, loading up your Home Assistant frontend on a new *device* will create three to five new devices.
|
||||
|
||||
- `sensor.<device>`
|
||||
- `media_player.<device>`
|
||||
- `light.<device>`
|
||||
- If you're using Fully Kiosk Browser `binary_sensor.<device>`
|
||||
- If you've enabled it: `camera.<device>`
|
||||
- If you're using Fully Kiosk Browser: `binary_sensor.<device>`
|
||||
|
||||
`<device>` here will be the `deviceID` of the *device* but with the dash (`-`) replaced by an underscore (`_`). If you've defined an alias, it will be that instead.
|
||||
|
||||
@ -105,6 +106,24 @@ The `light` can be used to blackout the screen.
|
||||
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).
|
||||
|
||||
### camera (EXPERIMENTAL)
|
||||
|
||||
For security and UX reasons, the camera must be enabled manually on a device by device basis.
|
||||
|
||||
Enabling the camera is done by adding `camera: true` to the devices configuration in `configuration.yaml`:
|
||||
```yaml
|
||||
browser_mod:
|
||||
devices:
|
||||
99980b13-dabc9563:
|
||||
name: arrakis
|
||||
camera: true
|
||||
d2fc860c-16379d23:
|
||||
name: dashboard
|
||||
```
|
||||
After restarting Home Assistant (and [clearing cache](https://github.com/thomasloven/hass-config/wiki/Lovelace-Plugins#clearing-cache)), the next time you load your interface your browser will ask you if you want Home Assistant to be able to access your camera. Some browsers (e.g. mobile Safari) will ask every time you make a hard refresh.
|
||||
|
||||
Be aware that keeping the camera on may make your device run hot and drain your battery.
|
||||
|
||||
### binary\_sensor
|
||||
|
||||
The `binary_sensor` will only be available for Fully Kiosk Browser PRO *devices*.
|
||||
|
@ -3,7 +3,7 @@ import logging
|
||||
from .mod_view import setup_view
|
||||
from .connection import setup_connection
|
||||
from .service import setup_service
|
||||
from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, DATA_ADDERS, CONFIG_DEVICES
|
||||
from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, DATA_ADDERS, CONFIG_DEVICES, DATA_CONFIG
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -22,12 +22,14 @@ async def async_setup(hass, config):
|
||||
DATA_DEVICES: {},
|
||||
DATA_ALIASES: aliases,
|
||||
DATA_ADDERS: {},
|
||||
DATA_CONFIG: config[DOMAIN].get(CONFIG_DEVICES, {}),
|
||||
}
|
||||
|
||||
await hass.helpers.discovery.async_load_platform("media_player", DOMAIN, {}, config)
|
||||
await hass.helpers.discovery.async_load_platform("sensor", DOMAIN, {}, config)
|
||||
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)
|
||||
|
||||
await setup_connection(hass, config)
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
36
custom_components/browser_mod/camera.py
Normal file
36
custom_components/browser_mod/camera.py
Normal file
@ -0,0 +1,36 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import base64
|
||||
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_ON, STATE_OFF, STATE_IDLE
|
||||
from homeassistant.components.camera import Camera
|
||||
|
||||
from .helpers import setup_platform, BrowserModEntity
|
||||
|
||||
PLATFORM = 'camera'
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||
return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModCamera)
|
||||
|
||||
class BrowserModCamera(Camera, BrowserModEntity):
|
||||
domain = PLATFORM
|
||||
|
||||
def __init__(self, hass, connection, deviceID, alias=None):
|
||||
Camera.__init__(self)
|
||||
BrowserModEntity.__init__(self, hass, connection, deviceID, alias)
|
||||
self.last_seen = None
|
||||
|
||||
def updated(self):
|
||||
self.last_seen = datetime.now()
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def camera_image(self):
|
||||
return base64.b64decode(self.data.split(',')[1])
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
return {
|
||||
"type": "browser_mod",
|
||||
"deviceID": self.deviceID,
|
||||
"last_seen": self.last_seen,
|
||||
}
|
@ -4,8 +4,8 @@ import voluptuous as vol
|
||||
from homeassistant.components.websocket_api import websocket_command, result_message, event_message, async_register_command
|
||||
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
||||
|
||||
from .const import DOMAIN, WS_CONNECT, WS_UPDATE
|
||||
from .helpers import get_devices, create_entity
|
||||
from .const import DOMAIN, WS_CONNECT, WS_UPDATE, WS_CAMERA
|
||||
from .helpers import get_devices, create_entity, get_config
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -38,7 +38,6 @@ async def setup_connection(hass, config):
|
||||
async_register_command(hass, handle_connect)
|
||||
async_register_command(hass, handle_update)
|
||||
|
||||
|
||||
class BrowserModConnection:
|
||||
def __init__(self, hass, deviceID):
|
||||
self.hass = hass
|
||||
@ -49,10 +48,11 @@ class BrowserModConnection:
|
||||
self.screen = None
|
||||
self.sensor = None
|
||||
self.fully = None
|
||||
self.camera = None
|
||||
|
||||
def connect(self, connection, cid):
|
||||
self.connection.append((connection, cid))
|
||||
self.send("update")
|
||||
self.send("update", **get_config(self.hass, self.deviceID))
|
||||
|
||||
def disconnect():
|
||||
self.connection.remove((connection, cid))
|
||||
@ -100,3 +100,11 @@ class BrowserModConnection:
|
||||
self)
|
||||
self.fully.data = data.get('fully')
|
||||
|
||||
if data.get('camera'):
|
||||
self.camera = self.camera or create_entity(
|
||||
self.hass,
|
||||
'camera',
|
||||
self.deviceID,
|
||||
self)
|
||||
self.camera.data = data.get('camera')
|
||||
|
||||
|
@ -7,9 +7,11 @@ DATA_EXTRA_MODULE_URL = 'frontend_extra_module_url'
|
||||
DATA_DEVICES = "devices"
|
||||
DATA_ALIASES = "aliases"
|
||||
DATA_ADDERS = "adders"
|
||||
DATA_CONFIG = "config"
|
||||
|
||||
CONFIG_DEVICES = "devices"
|
||||
|
||||
WS_ROOT = DOMAIN
|
||||
WS_CONNECT = "{}/connect".format(WS_ROOT)
|
||||
WS_UPDATE = "{}/update".format(WS_ROOT)
|
||||
WS_CAMERA = "{}/camera".format(WS_ROOT)
|
||||
|
@ -2,7 +2,7 @@ import logging
|
||||
|
||||
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
||||
|
||||
from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, DATA_ADDERS, CONFIG_DEVICES
|
||||
from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, DATA_ADDERS, CONFIG_DEVICES, DATA_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -15,6 +15,10 @@ def get_alias(hass, deviceID):
|
||||
return k
|
||||
return None
|
||||
|
||||
def get_config(hass, deviceID):
|
||||
config = hass.data[DOMAIN][DATA_CONFIG]
|
||||
return config.get(deviceID, config.get(deviceID.replace('-','_'), {}))
|
||||
|
||||
def create_entity(hass, platform, deviceID, connection):
|
||||
adder = hass.data[DOMAIN][DATA_ADDERS][platform]
|
||||
entity = adder(hass, deviceID, connection, get_alias(hass, deviceID))
|
||||
|
@ -37,6 +37,8 @@ class BrowserModLight(Light, BrowserModEntity):
|
||||
def device_state_attributes(self):
|
||||
return {
|
||||
"type": "browser_mod",
|
||||
"deviceID": self.deviceID,
|
||||
"last_seen": self.last_seen,
|
||||
}
|
||||
|
||||
@property
|
||||
|
50
js/main.js
50
js/main.js
@ -27,6 +27,7 @@ class BrowserMod {
|
||||
}
|
||||
|
||||
playOnce(ev) {
|
||||
if(this._video) this._video.play();
|
||||
if(window.browser_mod.playedOnce) return;
|
||||
window.browser_mod.player.play();
|
||||
window.browser_mod.playedOnce = true;
|
||||
@ -297,12 +298,57 @@ class BrowserMod {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
if(!this.conn) return;
|
||||
|
||||
if(msg)
|
||||
if(msg.entity_id)
|
||||
if(msg) {
|
||||
if(msg.entity_id) {
|
||||
this.entity_id = msg.entity_id;
|
||||
}
|
||||
if(msg.camera) {
|
||||
this.start_camera();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.conn.sendMessage({
|
||||
type: 'browser_mod/update',
|
||||
|
Loading…
x
Reference in New Issue
Block a user