diff --git a/README.md b/README.md
index b832c4df..57235293 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[![Build Status](https://github.com/aneisch/home-assistant-config/actions/workflows/check-ha-release-compatibility.yml/badge.svg)](https://github.com/aneisch/home-assistant-config/actions)
[![GitHub last commit](https://img.shields.io/github/last-commit/aneisch/home-assistant-config)](https://github.com/aneisch/home-assistant-config/commits/master)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/aneisch/home-assistant-config)](https://github.com/aneisch/home-assistant-config/graphs/commit-activity)
-[![HA Version](https://img.shields.io/badge/Running%20Home%20Assistant-2023.8.2%20(Latest)-brightgreen)](https://github.com/home-assistant/home-assistant/releases/latest)
+[![HA Version](https://img.shields.io/badge/Running%20Home%20Assistant-2023.8.4%20(Latest)-brightgreen)](https://github.com/home-assistant/home-assistant/releases/latest)
@@ -56,7 +56,7 @@ Also using Grafana/Influx for graphing, both running in Docker containers on NUC
Description | value
-- | --
Lines of ESPHome YAML | 2353
-Lines of Home Assistant YAML | 7759
+Lines of Home Assistant YAML | 7787
[Integrations](https://www.home-assistant.io/integrations/) in use | 50
Zigbee devices in [`zha`](https://www.home-assistant.io/integrations/zha/) | 26
Z-Wave devices in [`zwave_js`](https://www.home-assistant.io/integrations/zwave_js/) | 37
@@ -65,10 +65,10 @@ Description | value
-- | --
Entities in the [`alarm_control_panel`](https://www.home-assistant.io/components/alarm_control_panel) domain | 1
Entities in the [`automation`](https://www.home-assistant.io/components/automation) domain | 98
-Entities in the [`binary_sensor`](https://www.home-assistant.io/components/binary_sensor) domain | 116
-Entities in the [`button`](https://www.home-assistant.io/components/button) domain | 11
+Entities in the [`binary_sensor`](https://www.home-assistant.io/components/binary_sensor) domain | 117
+Entities in the [`button`](https://www.home-assistant.io/components/button) domain | 12
Entities in the [`calendar`](https://www.home-assistant.io/components/calendar) domain | 1
-Entities in the [`camera`](https://www.home-assistant.io/components/camera) domain | 16
+Entities in the [`camera`](https://www.home-assistant.io/components/camera) domain | 17
Entities in the [`climate`](https://www.home-assistant.io/components/climate) domain | 1
Entities in the [`counter`](https://www.home-assistant.io/components/counter) domain | 1
Entities in the [`cover`](https://www.home-assistant.io/components/cover) domain | 8
@@ -81,7 +81,7 @@ Entities in the [`input_number`](https://www.home-assistant.io/components/input_
Entities in the [`input_select`](https://www.home-assistant.io/components/input_select) domain | 19
Entities in the [`input_text`](https://www.home-assistant.io/components/input_text) domain | 5
Entities in the [`light`](https://www.home-assistant.io/components/light) domain | 37
-Entities in the [`lock`](https://www.home-assistant.io/components/lock) domain | 2
+Entities in the [`lock`](https://www.home-assistant.io/components/lock) domain | 3
Entities in the [`media_player`](https://www.home-assistant.io/components/media_player) domain | 12
Entities in the [`number`](https://www.home-assistant.io/components/number) domain | 5
Entities in the [`person`](https://www.home-assistant.io/components/person) domain | 2
@@ -90,17 +90,17 @@ Entities in the [`remote`](https://www.home-assistant.io/components/remote) doma
Entities in the [`scene`](https://www.home-assistant.io/components/scene) domain | 2
Entities in the [`script`](https://www.home-assistant.io/components/script) domain | 39
Entities in the [`select`](https://www.home-assistant.io/components/select) domain | 2
-Entities in the [`sensor`](https://www.home-assistant.io/components/sensor) domain | 360
+Entities in the [`sensor`](https://www.home-assistant.io/components/sensor) domain | 362
Entities in the [`siren`](https://www.home-assistant.io/components/siren) domain | 1
Entities in the [`sun`](https://www.home-assistant.io/components/sun) domain | 1
-Entities in the [`switch`](https://www.home-assistant.io/components/switch) domain | 149
+Entities in the [`switch`](https://www.home-assistant.io/components/switch) domain | 147
Entities in the [`timer`](https://www.home-assistant.io/components/timer) domain | 2
Entities in the [`tts`](https://www.home-assistant.io/components/tts) domain | 1
Entities in the [`update`](https://www.home-assistant.io/components/update) domain | 2
Entities in the [`vacuum`](https://www.home-assistant.io/components/vacuum) domain | 1
Entities in the [`weather`](https://www.home-assistant.io/components/weather) domain | 2
Entities in the [`zone`](https://www.home-assistant.io/components/zone) domain | 6
-**Total state objects** | **990**
+**Total state objects** | **994**
## The HACS integrations/plugins that I use:
**Appdaemon**:
[aneisch/follow_me_appdaemon](https://github.com/aneisch/follow_me_appdaemon)
diff --git a/automations.yaml b/automations.yaml
index 2da957f0..d0dbebb7 100644
--- a/automations.yaml
+++ b/automations.yaml
@@ -492,7 +492,8 @@
- sensor.2_battery_level
- sensor.3_battery_level
- sensor.back_bedroom_remote_battery_level
- - sensor.front_door_battery_level
+ - sensor.front_door_battery
+ - sensor.back_door_battery
- sensor.garage_exterior_door_battery_level
- sensor.back_bathroom_toilet_water_detector_battery_level
- sensor.guest_bathroom_toilet_water_detector_battery_level
@@ -677,7 +678,7 @@
condition: "{{ states('sensor.fingerprint_last_finger_id') in ['1','2'] }}"
action:
- service: lock.unlock
- entity_id: lock.front_door
+ entity_id: lock.back_door
- service: script.notify_wrapper
data:
message: "Front door unlocked by {% if states('sensor.fingerprint_last_finger_id') == '1' %}Andrew right index{% elif states('sensor.fingerprint_last_finger_id') == '2' %}Andrew left index{% else %}Unknown{% endif %}!"
diff --git a/configuration.yaml b/configuration.yaml
index be5453c1..ecf3faf9 100644
--- a/configuration.yaml
+++ b/configuration.yaml
@@ -524,6 +524,11 @@ timer:
tts:
- platform: picotts
+ - platform: microsoft
+ api_key: !secret AzureSpeechKey
+ region: centralus
+ gender: Male
+ type: GuyNeural
wemo:
discovery: false
@@ -537,7 +542,7 @@ utility_meter:
cron: "0 22 L * *"
zha:
- enable_quirks: true
+ #enable_quirks: true
#custom_quirks_path: /config/custom_zha_quirks/
zigpy_config:
network:
diff --git a/custom_components/webrtc/__init__.py b/custom_components/webrtc/__init__.py
index 7409790a..6f488efc 100644
--- a/custom_components/webrtc/__init__.py
+++ b/custom_components/webrtc/__init__.py
@@ -3,6 +3,7 @@
import time
import uuid
from pathlib import Path
+from typing import Union
from urllib.parse import urlencode, urljoin
import homeassistant.helpers.config_validation as cv
@@ -43,6 +44,7 @@
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
vol.Exclusive("url", "url"): cv.string,
vol.Exclusive("entity", "url"): cv.entity_id,
+ vol.Optional("force", default=False): bool,
vol.Optional("extra"): dict,
},
required=True,
@@ -105,6 +107,7 @@ async def dash_cast(call: ServiceCallType):
hass,
call.data[ATTR_ENTITY_ID],
f"{get_url(hass)}/webrtc/embed?" + urlencode(query),
+ call.data.get("force"),
)
hass.services.async_register(DOMAIN, "create_link", create_link, CREATE_LINK_SCHEMA)
@@ -146,44 +149,29 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
return True
-async def ws_connect(hass: HomeAssistantType, params) -> str:
- entry = hass.data[DOMAIN]
- if isinstance(entry, Server):
- assert entry.available, "WebRTC server not available"
- go_url = "http://localhost:1984/"
- else:
- go_url = entry
-
- if entity := params.get("entity"):
- src = await utils.get_stream_source(hass, entity)
- assert src, f"Can't get URL for {entity}"
-
- # adds stream to go2rtc using entity_id as name (RTSPtoWebRTC API)
- session = async_get_clientsession(hass)
- r = await session.patch(
- urljoin(go_url, "api/streams"),
- params={"name": entity, "src": src},
- timeout=3,
- )
- assert r.ok, f"Can't update URL for {entity}"
- src = entity
+async def ws_connect(hass: HomeAssistantType, params: dict) -> str:
+ # 1. Server URL from card param
+ server: str = params.get("server")
+ # 2. Server URL from integration settings
+ if not server:
+ server: Union[str, Server] = hass.data[DOMAIN]
+ # 3. Server is manual binary
+ if isinstance(server, Server):
+ assert server.available, "WebRTC server not available"
+ server = "http://localhost:1984/"
+ if name := params.get("entity"):
+ src = await utils.get_stream_source(hass, name)
+ assert src, f"Can't get URL for {name}"
+ query = {"src": src, "name": name}
elif src := params.get("url"):
if "{{" in src or "{%" in src:
src = Template(src, hass).async_render()
-
- session = async_get_clientsession(hass)
- r = await session.patch(
- urljoin(go_url, "api/streams"),
- params={"name": src, "src": src},
- timeout=3,
- )
- assert r.ok, f"Can't update URL for {src}"
-
+ query = {"src": src}
else:
raise Exception("Missing url or entity")
- return urljoin("ws" + go_url[4:], "api/ws") + "?" + urlencode({"src": src})
+ return urljoin("ws" + server[4:], "api/ws") + "?" + urlencode(query)
class WebSocketView(HomeAssistantView):
diff --git a/custom_components/webrtc/manifest.json b/custom_components/webrtc/manifest.json
index 6418b2d7..202d42af 100644
--- a/custom_components/webrtc/manifest.json
+++ b/custom_components/webrtc/manifest.json
@@ -12,6 +12,6 @@
"lovelace"
],
"requirements": [],
- "version": "v3.2.1",
+ "version": "v3.3.0",
"iot_class": "calculated"
}
\ No newline at end of file
diff --git a/custom_components/webrtc/services.yaml b/custom_components/webrtc/services.yaml
index a5f36e12..86ac6ddb 100644
--- a/custom_components/webrtc/services.yaml
+++ b/custom_components/webrtc/services.yaml
@@ -61,6 +61,11 @@ dash_cast:
selector:
text:
+ force:
+ description: Force restart DashCast application
+ selector:
+ boolean:
+
entity:
description: Camera entity
example: camera.generic_stream
diff --git a/custom_components/webrtc/utils.py b/custom_components/webrtc/utils.py
index 2f51418d..15140a81 100644
--- a/custom_components/webrtc/utils.py
+++ b/custom_components/webrtc/utils.py
@@ -24,7 +24,7 @@
DOMAIN = "webrtc"
-BINARY_VERSION = "1.6.0"
+BINARY_VERSION = "1.6.2"
SYSTEM = {
"Windows": {"AMD64": "go2rtc_win64.zip", "ARM64": "go2rtc_win_arm64.zip"},
@@ -169,7 +169,7 @@ async def init_resource(hass: HomeAssistant, url: str, ver: str) -> bool:
# noinspection PyProtectedMember
-def dash_cast(hass: HomeAssistant, cast_entities: list, url: str):
+def dash_cast(hass: HomeAssistant, cast_entities: list, url: str, force=False):
"""Cast webpage to chromecast device via DashCast application."""
try:
entities = [
@@ -188,7 +188,7 @@ def dash_cast(hass: HomeAssistant, cast_entities: list, url: str):
entity._chromecast.register_handler(entity.dashcast)
_LOGGER.debug(f"DashCast to {entity.entity_id}")
- entity.dashcast.load_url(url)
+ entity.dashcast.load_url(url, force)
except Exception:
_LOGGER.exception(f"Can't DashCast to {cast_entities}")
diff --git a/custom_components/webrtc/www/digital-ptz.js b/custom_components/webrtc/www/digital-ptz.js
index a47b5eae..684bcce8 100644
--- a/custom_components/webrtc/www/digital-ptz.js
+++ b/custom_components/webrtc/www/digital-ptz.js
@@ -13,7 +13,7 @@ const DEFAULT_OPTIONS = {
persist: true,
};
export class DigitalPTZ {
- constructor(containerEl, videoEl, options) {
+ constructor(containerEl, transformEl, videoEl, options) {
this.offHandles = [];
this.recomputeRects = () => {
this.transform.updateRects(this.videoEl, this.containerEl);
@@ -23,14 +23,15 @@ export class DigitalPTZ {
this.render = (transition = false) => {
if (transition) {
// transition is used to animate dbl click zoom
- this.videoEl.style.transition = "transform 200ms";
+ this.transformEl.style.transition = "transform 200ms";
setTimeout(() => {
- this.videoEl.style.transition = "";
+ this.transformEl.style.transition = "";
}, 200);
}
- this.videoEl.style.transform = this.transform.render();
+ this.transformEl.style.transform = this.transform.render();
};
this.containerEl = containerEl;
+ this.transformEl = transformEl;
this.videoEl = videoEl;
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
this.transform = new Transform({
@@ -179,11 +180,12 @@ function startGesturePan({ containerEl, transform, render }, type) {
type === "mouse"
? ["mousedown", "mousemove", "mouseup"]
: ["touchstart", "touchmove", "touchend"];
+ const isTouchEvent = ev => 'TouchEvent' in window && ev instanceof TouchEvent;
const onDown = (downEvt) => {
- let last = downEvt instanceof TouchEvent ? downEvt.touches[0] : downEvt;
+ let last = isTouchEvent(downEvt) ? downEvt.touches[0] : downEvt;
const onMove = (moveEvt) => {
- if (moveEvt instanceof TouchEvent && moveEvt.touches.length !== 1) return;
- const curr = moveEvt instanceof TouchEvent ? moveEvt.touches[0] : moveEvt;
+ if (isTouchEvent(moveEvt) && moveEvt.touches.length !== 1) return;
+ const curr = isTouchEvent(moveEvt) ? moveEvt.touches[0] : moveEvt;
transform.move(curr.pageX - last.pageX, curr.pageY - last.pageY);
last = curr;
render();
@@ -205,6 +207,25 @@ function startMouseDragPan(params) {
/** Transform */
const PERSIST_KEY_PREFIX = "webrtc-digital-ptc:";
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
+function getTransformedDimensions(video) {
+ const { videoWidth, videoHeight } = video;
+ if (!videoHeight || !videoWidth) return undefined;
+ var transform = window.getComputedStyle(video).getPropertyValue("transform");
+ const match = transform.match(/matrix\((.+)\)/);
+ if (!match || !match[1]) return { videoWidth, videoHeight }; // the video isn't transformed
+ const matrix = new DOMMatrix(match[1].split(", ").map(Number));
+ const points = [
+ new DOMPoint(0, 0),
+ new DOMPoint(videoWidth, 0),
+ new DOMPoint(0, videoHeight),
+ new DOMPoint(videoWidth, videoHeight),
+ ].map((point) => point.matrixTransform(matrix));
+ const minX = Math.min(...points.map((point) => point.x));
+ const maxX = Math.max(...points.map((point) => point.x));
+ const minY = Math.min(...points.map((point) => point.y));
+ const maxY = Math.max(...points.map((point) => point.y));
+ return { videoWidth: maxX - minX, videoHeight: maxY - minY };
+}
class Transform {
constructor(settings) {
this.scale = 1;
@@ -252,7 +273,8 @@ class Transform {
return;
}
this.containerRect = containerRect;
- if (!videoEl.videoWidth) {
+ const transformed = getTransformedDimensions(videoEl);
+ if (!transformed) {
// The video hasn't loaded yet.
// Once it loads, the videometadata listener will call this function again.
return;
@@ -262,7 +284,7 @@ class Transform {
// This needs to be accounted for when panning, the code below keeps track of that.
const screenAspectRatio =
this.containerRect.width / this.containerRect.height;
- const videoAspectRatio = videoEl.videoWidth / videoEl.videoHeight;
+ const videoAspectRatio = transformed.videoWidth / transformed.videoHeight;
if (videoAspectRatio > screenAspectRatio) {
// Black bars on the top and bottom
const videoHeight = this.containerRect.width / videoAspectRatio;
diff --git a/custom_components/webrtc/www/digital-ptz.js.gz b/custom_components/webrtc/www/digital-ptz.js.gz
index fcd054ae..128e0d24 100644
Binary files a/custom_components/webrtc/www/digital-ptz.js.gz and b/custom_components/webrtc/www/digital-ptz.js.gz differ
diff --git a/custom_components/webrtc/www/video-rtc.js b/custom_components/webrtc/www/video-rtc.js
index 5f6b778d..a259962e 100644
--- a/custom_components/webrtc/www/video-rtc.js
+++ b/custom_components/webrtc/www/video-rtc.js
@@ -27,7 +27,6 @@ export class VideoRTC extends HTMLElement {
'hvc1.1.6.L153.B0', // H.265 main 5.1 (Chromecast Ultra)
'mp4a.40.2', // AAC LC
'mp4a.40.5', // AAC HE
- 'null', // for detecting liars (Safari iOS 12)
'flac', // FLAC (PCM compatible)
'opus', // OPUS Chrome, Firefox
];
@@ -241,6 +240,14 @@ export class VideoRTC extends HTMLElement {
this.appendChild(this.video);
+ // all Safari lies about supported audio codecs
+ const m = window.navigator.userAgent.match(/Version\/(\d+).+Safari/);
+ if (m) {
+ // AAC from v13, FLAC from v14, OPUS - unsupported
+ const skip = m[1] < '13' ? 'mp4a.40.2' : m[1] < '14' ? 'flac' : 'opus';
+ this.CODECS.splice(this.CODECS.indexOf(skip));
+ }
+
if (this.background) return;
if ('hidden' in document && this.visibilityCheck) {
@@ -544,12 +551,16 @@ export class VideoRTC extends HTMLElement {
this.pcState = WebSocket.OPEN;
this.wsState = WebSocket.CLOSED;
- this.ws.close();
- this.ws = null;
+ if (this.ws) {
+ this.ws.close();
+ this.ws = null;
+ }
} else {
this.pcState = WebSocket.CLOSED;
- this.pc.close();
- this.pc = null;
+ if (this.pc) {
+ this.pc.close();
+ this.pc = null;
+ }
}
}
diff --git a/custom_components/webrtc/www/video-rtc.js.gz b/custom_components/webrtc/www/video-rtc.js.gz
index 9cd6bb0f..d0a97da5 100644
Binary files a/custom_components/webrtc/www/video-rtc.js.gz and b/custom_components/webrtc/www/video-rtc.js.gz differ
diff --git a/custom_components/webrtc/www/webrtc-camera.js b/custom_components/webrtc/www/webrtc-camera.js
index 3f4a6895..36f0fbf1 100644
--- a/custom_components/webrtc/www/webrtc-camera.js
+++ b/custom_components/webrtc/www/webrtc-camera.js
@@ -1,6 +1,6 @@
/** Chrome 63+, Safari 11.1+ */
-import {VideoRTC} from './video-rtc.js?v=1.6.0';
-import {DigitalPTZ} from './digital-ptz.js?v3.1.1';
+import {VideoRTC} from './video-rtc.js?v=1.6.2';
+import {DigitalPTZ} from './digital-ptz.js?v=3.3.0';
class WebRTCCamera extends VideoRTC {
/**
@@ -8,41 +8,71 @@ class WebRTCCamera extends VideoRTC {
* @param {Object} config
*/
setConfig(config) {
- if (!config.url && !config.entity) throw new Error('Missing `url` or `entity`');
-
- if (config.mode) this.mode = config.mode;
- // backward compatibility
- else if (config.mse === false) this.mode = 'webrtc';
- else if (config.webrtc === false) this.mode = 'mse';
+ if (!config.url && !config.entity && !config.streams) throw new Error('Missing `url` or `entity` or `streams`');
if (config.background) this.background = config.background;
if (config.intersection === 0) this.visibilityThreshold = 0;
else this.visibilityThreshold = config.intersection || 0.75;
+ /** @type {string} configMode */
+ this.configMode = config.mode
+ ? config.mode
+ : config.mse === false
+ ? 'webrtc'
+ : config.webrtc === false
+ ? 'mse'
+ : this.mode;
+
/**
* @type {{
- * url:string, entity:string, muted:boolean, poster:string, title:string,
- * intersection:number, ui:boolean, style:string,
+ * url: string,
+ * entity: string,
+ * mode: string,
+ * server: string,
+ *
+ * streams: Array<{
+ * name: string,
+ * url: string,
+ * entity: string,
+ * mode: string
+ * }>,
+ *
+ * title: string,
+ * poster: string,
+ * muted: boolean,
+ * intersection: number,
+ * ui: boolean,
+ * style: string,
+ *
+ * mse: boolean,
+ * webrtc: boolean,
+ *
* digital_ptz:{
- * mouse_drag_pan:boolean,
- * mouse_wheel_zoom:boolean,
- * mouse_double_click_zoom:boolean,
- * touch_pinch_zoom:boolean,
- * touch_drag_pan:boolean,
- * touch_tap_drag_zoom:boolean,
- * persist:boolean|string,
+ * mouse_drag_pan: boolean,
+ * mouse_wheel_zoom: boolean,
+ * mouse_double_click_zoom: boolean,
+ * touch_pinch_zoom: boolean,
+ * touch_drag_pan: boolean,
+ * touch_tap_drag_zoom: boolean,
+ * persist: boolean|string,
* },
* ptz:{
- * opacity:number|string, service:string,
- * data_left, data_up, data_right, data_down,
- * data_zoom_in, data_zoom_out, data_home
+ * opacity: number|string,
+ * service: string,
+ * data_left, data_up, data_right, data_down, data_zoom_in, data_zoom_out, data_home
* },
- * shortcuts:Array<{name:string,icon:string}>,
- * mse:boolean, webrtc:boolean,
+ * shortcuts:Array<{ name:string, icon:string }>,
* }} config
*/
- this.config = config;
+ this.config = Object.assign({}, config);
+
+ if (!this.config.streams) {
+ this.config.streams = [{url: config.url, entity: config.entity}];
+ }
+
+ this.streamID = -1;
+ this.nextStream(false);
}
set hass(hass) {
@@ -60,7 +90,7 @@ class WebRTCCamera extends VideoRTC {
/**
* Called by the Hass to get defaul card config
- * @returns {{url: string}}
+ * @return {{url: string}}
*/
static getStubConfig() {
return {'url': ''};
@@ -68,12 +98,31 @@ class WebRTCCamera extends VideoRTC {
setStatus(mode, status) {
const divMode = this.querySelector('.mode').innerText;
- if (mode === 'error' && divMode !== 'loading1' && divMode !== 'loading2') return;
+ if (mode === 'error' && divMode !== 'Loading..' && divMode !== 'Loading...') return;
this.querySelector('.mode').innerText = mode;
this.querySelector('.status').innerText = status || '';
}
+ /** @param reload {boolean} */
+ nextStream(reload) {
+ this.streamID = (this.streamID + 1) % this.config.streams.length;
+ const stream = this.config.streams[this.streamID];
+ this.config.url = stream.url;
+ this.config.entity = stream.entity;
+ this.mode = stream.mode || this.configMode;
+
+ if (reload) {
+ this.ondisconnect();
+ setTimeout(() => this.onconnect(), 100); // wait ws.close event
+ }
+ }
+
+ /** @return {string} */
+ get streamName() {
+ return this.config.streams[this.streamID].name || `S${this.streamID}`;
+ }
+
oninit() {
super.oninit();
this.renderMain();
@@ -89,9 +138,9 @@ class WebRTCCamera extends VideoRTC {
if (!this.isConnected || this.ws || this.pc) return false;
const divMode = this.querySelector('.mode').innerText;
- if (divMode === 'loading1') return;
+ if (divMode === 'Loading..') return;
- this.setStatus('loading1');
+ this.setStatus('Loading..');
this.hass.callWS({
type: 'auth/sign_path', path: '/api/webrtc/ws'
@@ -100,11 +149,14 @@ class WebRTCCamera extends VideoRTC {
if (this.config.url) {
this.wsURL += '&url=' + encodeURIComponent(this.config.url);
}
+ if (this.config.server) {
+ this.wsURL += '&server=' + encodeURIComponent(this.config.server);
+ }
if (this.config.entity) {
this.wsURL += '&entity=' + this.config.entity;
}
if (super.onconnect()) {
- this.setStatus('loading2');
+ this.setStatus('Loading...');
} else {
this.setStatus('error', 'unable to connect');
}
@@ -160,13 +212,12 @@ class WebRTCCamera extends VideoRTC {
background-color: black;
height: 100%;
position: relative; /* important for Safari */
- overflow: hidden; /* important for zoom-controller */
}
.player:active {
cursor: move; /* important for zoom-controller */
}
- video {
- transform-origin: 50% 50%; /* important for zoom-controller */
+ .player .ptz-transform {
+ height: 100%;
}
.header {
position: absolute;
@@ -179,11 +230,15 @@ class WebRTCCamera extends VideoRTC {
pointer-events: none;
}
.mode {
+ cursor: pointer;
opacity: 0.6;
+ pointer-events: auto;
}
-
+