Browser state sensors and Visit device for FKB
This commit is contained in:
parent
1109980d61
commit
a46d2b3cb0
@ -937,6 +937,8 @@ const MediaPlayerMixin = (SuperClass) => {
|
|||||||
|
|
||||||
const CameraMixin = (SuperClass) => {
|
const CameraMixin = (SuperClass) => {
|
||||||
return class CameraMixinClass extends SuperClass {
|
return class CameraMixinClass extends SuperClass {
|
||||||
|
// TODO: Enable WebRTC?
|
||||||
|
// https://levelup.gitconnected.com/establishing-the-webrtc-connection-videochat-with-javascript-step-3-48d4ae0e9ea4
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._framerate = 2;
|
this._framerate = 2;
|
||||||
@ -1041,6 +1043,46 @@ const RequireInteractMixin = (SuperClass) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const BrowserStateMixin = (SuperClass) => {
|
||||||
|
return class BrowserStateMixinClass extends SuperClass {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
document.addEventListener("visibilitychange", () => this._browser_state_update());
|
||||||
|
window.addEventListener("location-changed", () => this._browser_state_update());
|
||||||
|
this.connectionPromise.then(() => this._browser_state_update());
|
||||||
|
}
|
||||||
|
_browser_state_update() {
|
||||||
|
const update = async () => {
|
||||||
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
||||||
|
const battery = (_b = (_a = navigator).getBattery) === null || _b === void 0 ? void 0 : _b.call(_a);
|
||||||
|
this.sendUpdate({
|
||||||
|
browser: {
|
||||||
|
path: window.location.pathname,
|
||||||
|
visibility: document.visibilityState,
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
|
currentUser: (_d = (_c = this.hass) === null || _c === void 0 ? void 0 : _c.user) === null || _d === void 0 ? void 0 : _d.name,
|
||||||
|
fullyKiosk: this.isFully || false,
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
battery_level: (_f = (_e = window.fully) === null || _e === void 0 ? void 0 : _e.getBatteryLevel()) !== null && _f !== void 0 ? _f : (battery === null || battery === void 0 ? void 0 : battery.level) * 100,
|
||||||
|
charging: (_h = (_g = window.fully) === null || _g === void 0 ? void 0 : _g.isPlugged()) !== null && _h !== void 0 ? _h : battery === null || battery === void 0 ? void 0 : battery.charging,
|
||||||
|
darkMode: (_k = (_j = this.hass) === null || _j === void 0 ? void 0 : _j.themes) === null || _k === void 0 ? void 0 : _k.darkMode,
|
||||||
|
userData: (_l = this.hass) === null || _l === void 0 ? void 0 : _l.user,
|
||||||
|
ip_address: (_m = window.fully) === null || _m === void 0 ? void 0 : _m.getIp4Address(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
do_navigate(path) {
|
||||||
|
if (!path)
|
||||||
|
return;
|
||||||
|
history.pushState(null, "", path);
|
||||||
|
fireEvent("location-changed", {}, ha_element());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var name = "browser_mod";
|
var name = "browser_mod";
|
||||||
var version = "2.0.0b0";
|
var version = "2.0.0b0";
|
||||||
var description = "";
|
var description = "";
|
||||||
@ -1088,7 +1130,7 @@ var pjson = {
|
|||||||
// FullyKioskMixin,
|
// FullyKioskMixin,
|
||||||
// BrowserModMediaPlayerMixin,
|
// BrowserModMediaPlayerMixin,
|
||||||
// ]) {
|
// ]) {
|
||||||
class BrowserMod extends CameraMixin(MediaPlayerMixin(ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget))))) {
|
class BrowserMod extends BrowserStateMixin(CameraMixin(MediaPlayerMixin(ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget)))))) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.entity_id = deviceID.replace("-", "_");
|
this.entity_id = deviceID.replace("-", "_");
|
||||||
|
@ -35,6 +35,5 @@ class BrowserModCamera(BrowserModEntity, Camera):
|
|||||||
|
|
||||||
def camera_image(self, width=None, height=None):
|
def camera_image(self, width=None, height=None):
|
||||||
if "camera" not in self._data:
|
if "camera" not in self._data:
|
||||||
LOGGER.error(self._data)
|
|
||||||
return None
|
return None
|
||||||
return base64.b64decode(self._data["camera"].split(",")[-1])
|
return base64.b64decode(self._data["camera"].split(",")[-1])
|
||||||
|
@ -128,8 +128,7 @@ async def async_setup_connection(hass):
|
|||||||
|
|
||||||
if store.get_device(deviceID).enabled:
|
if store.get_device(deviceID).enabled:
|
||||||
dev = getDevice(hass, deviceID)
|
dev = getDevice(hass, deviceID)
|
||||||
dev.data.update(msg.get("data", {}))
|
dev.update(hass, msg.get("data", {}))
|
||||||
dev.coordinator.async_set_updated_data(dev.data)
|
|
||||||
|
|
||||||
async_register_command(hass, handle_connect)
|
async_register_command(hass, handle_connect)
|
||||||
async_register_command(hass, handle_register)
|
async_register_command(hass, handle_register)
|
||||||
|
@ -21,77 +21,74 @@ class BrowserModDevice:
|
|||||||
""" """
|
""" """
|
||||||
self.deviceID = deviceID
|
self.deviceID = deviceID
|
||||||
self.coordinator = Coordinator(hass, deviceID)
|
self.coordinator = Coordinator(hass, deviceID)
|
||||||
self.entities = []
|
self.entities = {}
|
||||||
self.camera_entity = None
|
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.setup_sensors(hass)
|
self.settings = {}
|
||||||
self.connection = None
|
self.connection = None
|
||||||
|
|
||||||
def update_settings(self, hass, settings):
|
self.update_entities(hass)
|
||||||
if settings.get("camera", False) and self.camera_entity is None:
|
|
||||||
self.add_camera(hass)
|
|
||||||
elif self.camera_entity and not settings.get("camera", False):
|
|
||||||
self.remove_camera(hass)
|
|
||||||
|
|
||||||
def setup_sensors(self, hass):
|
def update(self, hass, newData):
|
||||||
|
self.data.update(newData)
|
||||||
|
self.update_entities(hass)
|
||||||
|
self.coordinator.async_set_updated_data(self.data)
|
||||||
|
|
||||||
|
def update_settings(self, hass, settings):
|
||||||
|
self.settings = settings
|
||||||
|
self.update_entities(hass)
|
||||||
|
|
||||||
|
def update_entities(self, hass):
|
||||||
"""Create all entities associated with the device."""
|
"""Create all entities associated with the device."""
|
||||||
|
|
||||||
coordinator = self.coordinator
|
coordinator = self.coordinator
|
||||||
deviceID = self.deviceID
|
deviceID = self.deviceID
|
||||||
|
|
||||||
sensors = [
|
def _assert_browser_sensor(type, name, *properties):
|
||||||
("battery_level", "Browser battery", "%", "battery"),
|
if name in self.entities:
|
||||||
("path", "Browser path"),
|
return
|
||||||
("userAgent", "Browser userAgent"),
|
adder = hass.data[DOMAIN][DATA_ADDERS][type]
|
||||||
("visibility", "Browser visibility"),
|
cls = {"sensor": BrowserSensor, "binary_sensor": BrowserBinarySensor}[type]
|
||||||
("currentUser", "Browser user"),
|
new = cls(coordinator, deviceID, name, *properties)
|
||||||
("height", "Browser height", "px"),
|
adder([new])
|
||||||
("width", "Browser width", "px"),
|
self.entities[name] = new
|
||||||
]
|
|
||||||
adder = hass.data[DOMAIN][DATA_ADDERS]["sensor"]
|
|
||||||
new = [BrowserSensor(coordinator, deviceID, *s) for s in sensors]
|
|
||||||
adder(new)
|
|
||||||
self.entities += new
|
|
||||||
|
|
||||||
binary_sensors = [
|
_assert_browser_sensor("sensor", "path", "Browser path")
|
||||||
("charging", "Browser charging"),
|
_assert_browser_sensor("sensor", "visibility", "Browser visibility")
|
||||||
("darkMode", "Browser dark mode"),
|
_assert_browser_sensor("sensor", "userAgent", "Browser userAgent")
|
||||||
("fullyKiosk", "Browser FullyKiosk"),
|
_assert_browser_sensor("sensor", "currentUser", "Browser user")
|
||||||
]
|
_assert_browser_sensor("sensor", "width", "Browser width", "px")
|
||||||
adder = hass.data[DOMAIN][DATA_ADDERS]["binary_sensor"]
|
_assert_browser_sensor("sensor", "height", "Browser height", "px")
|
||||||
new = [BrowserBinarySensor(coordinator, deviceID, *s) for s in binary_sensors]
|
if self.data.get("browser", {}).get("battery_level", None) is not None:
|
||||||
adder(new)
|
_assert_browser_sensor(
|
||||||
self.entities += new
|
"sensor", "battery_level", "Browser battery", "%", "battery"
|
||||||
|
)
|
||||||
|
|
||||||
adder = hass.data[DOMAIN][DATA_ADDERS]["light"]
|
_assert_browser_sensor("binary_sensor", "darkMode", "Browser dark mode")
|
||||||
new = [BrowserModLight(coordinator, deviceID, self)]
|
_assert_browser_sensor("binary_sensor", "fullyKiosk", "Browser FullyKiosk")
|
||||||
adder(new)
|
if self.data.get("browser", {}).get("charging", None) is not None:
|
||||||
self.entities += new
|
_assert_browser_sensor("binary_sensor", "charging", "Browser charging")
|
||||||
|
|
||||||
adder = hass.data[DOMAIN][DATA_ADDERS]["media_player"]
|
if "screen" not in self.entities:
|
||||||
new = [BrowserModPlayer(coordinator, deviceID, self)]
|
adder = hass.data[DOMAIN][DATA_ADDERS]["light"]
|
||||||
adder(new)
|
new = BrowserModLight(coordinator, deviceID, self)
|
||||||
self.entities += new
|
adder([new])
|
||||||
|
self.entities["screen"] = new
|
||||||
|
|
||||||
def add_camera(self, hass):
|
if "player" not in self.entities:
|
||||||
if self.camera_entity is not None:
|
adder = hass.data[DOMAIN][DATA_ADDERS]["media_player"]
|
||||||
return
|
new = BrowserModPlayer(coordinator, deviceID, self)
|
||||||
coordinator = self.coordinator
|
adder([new])
|
||||||
deviceID = self.deviceID
|
self.entities["player"] = new
|
||||||
adder = hass.data[DOMAIN][DATA_ADDERS]["camera"]
|
|
||||||
self.camera_entity = BrowserModCamera(coordinator, deviceID)
|
|
||||||
adder([self.camera_entity])
|
|
||||||
self.entities.append(self.camera_entity)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def remove_camera(self, hass):
|
if "camera" not in self.entities and self.settings.get("camera"):
|
||||||
if self.camera_entity is None:
|
adder = hass.data[DOMAIN][DATA_ADDERS]["camera"]
|
||||||
return
|
new = BrowserModCamera(coordinator, deviceID)
|
||||||
er = entity_registry.async_get(hass)
|
adder([new])
|
||||||
er.async_remove(self.camera_entity.entity_id)
|
self.entities["camera"] = new
|
||||||
self.entities.remove(self.camera_entity)
|
if "camera" in self.entities and not self.settings.get("camera"):
|
||||||
self.camera_entity = None
|
er = entity_registry.async_get(hass)
|
||||||
pass
|
er.async_remove(self.entities["camera"].entity_id)
|
||||||
|
del self.entities["camera"]
|
||||||
|
|
||||||
def send(self, command, **kwargs):
|
def send(self, command, **kwargs):
|
||||||
"""Send a command to this device."""
|
"""Send a command to this device."""
|
||||||
@ -115,11 +112,10 @@ class BrowserModDevice:
|
|||||||
dr = device_registry.async_get(hass)
|
dr = device_registry.async_get(hass)
|
||||||
er = entity_registry.async_get(hass)
|
er = entity_registry.async_get(hass)
|
||||||
|
|
||||||
for e in self.entities:
|
for e in self.entities.items():
|
||||||
er.async_remove(e.entity_id)
|
er.async_remove(e.entity_id)
|
||||||
|
|
||||||
self.entities = []
|
self.entities = {}
|
||||||
self.camera_entity = None
|
|
||||||
|
|
||||||
device = dr.async_get_device({(DOMAIN, self.deviceID)})
|
device = dr.async_get_device({(DOMAIN, self.deviceID)})
|
||||||
dr.async_remove_device(device.id)
|
dr.async_remove_device(device.id)
|
||||||
|
@ -22,10 +22,14 @@ class BrowserModEntity(CoordinatorEntity):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
|
config_url = {}
|
||||||
|
if ip := self._data.get("browser", {}).get("ip_address"):
|
||||||
|
config_url = {"configuration_url": f"http://{ip}:2323"}
|
||||||
return {
|
return {
|
||||||
"identifiers": {(DOMAIN, self.deviceID)},
|
"identifiers": {(DOMAIN, self.deviceID)},
|
||||||
"name": self.deviceID,
|
"name": self.deviceID,
|
||||||
"manufacturer": "Browser Mod",
|
"manufacturer": "Browser Mod",
|
||||||
|
**config_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
import { fireEvent } from "card-tools/src/event";
|
import { fireEvent } from "card-tools/src/event";
|
||||||
import { ha_element } from "card-tools/src/hass";
|
import { ha_element } from "card-tools/src/hass";
|
||||||
|
|
||||||
export const BrowserModBrowserMixin = (C) =>
|
export const BrowserStateMixin = (SuperClass) => {
|
||||||
class extends C {
|
return class BrowserStateMixinClass extends SuperClass {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
document.addEventListener("visibilitychange", () => this.sensor_update());
|
document.addEventListener("visibilitychange", () =>
|
||||||
window.addEventListener("location-changed", () => this.sensor_update());
|
this._browser_state_update()
|
||||||
|
|
||||||
this.addEventListener("browser-mod-connected", () =>
|
|
||||||
this.sensor_update()
|
|
||||||
);
|
);
|
||||||
// window.setInterval(() => this.sensor_update(), 10000);
|
window.addEventListener("location-changed", () =>
|
||||||
|
this._browser_state_update()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.connectionPromise.then(() => this._browser_state_update());
|
||||||
}
|
}
|
||||||
|
|
||||||
sensor_update() {
|
_browser_state_update() {
|
||||||
const update = async () => {
|
const update = async () => {
|
||||||
const battery = (<any>navigator).getBattery?.();
|
const battery = (<any>navigator).getBattery?.();
|
||||||
this.sendUpdate({
|
this.sendUpdate({
|
||||||
@ -23,7 +24,7 @@ export const BrowserModBrowserMixin = (C) =>
|
|||||||
visibility: document.visibilityState,
|
visibility: document.visibilityState,
|
||||||
userAgent: navigator.userAgent,
|
userAgent: navigator.userAgent,
|
||||||
currentUser: this.hass?.user?.name,
|
currentUser: this.hass?.user?.name,
|
||||||
fullyKiosk: this.isFully,
|
fullyKiosk: this.isFully || false,
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
battery_level:
|
battery_level:
|
||||||
@ -31,7 +32,7 @@ export const BrowserModBrowserMixin = (C) =>
|
|||||||
charging: window.fully?.isPlugged() ?? battery?.charging,
|
charging: window.fully?.isPlugged() ?? battery?.charging,
|
||||||
darkMode: this.hass?.themes?.darkMode,
|
darkMode: this.hass?.themes?.darkMode,
|
||||||
userData: this.hass?.user,
|
userData: this.hass?.user,
|
||||||
// config: this.config,
|
ip_address: window.fully?.getIp4Address(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -44,3 +45,4 @@ export const BrowserModBrowserMixin = (C) =>
|
|||||||
fireEvent("location-changed", {}, ha_element());
|
fireEvent("location-changed", {}, ha_element());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
@ -4,6 +4,9 @@ export const CameraMixin = (SuperClass) => {
|
|||||||
private _canvas;
|
private _canvas;
|
||||||
private _framerate;
|
private _framerate;
|
||||||
|
|
||||||
|
// TODO: Enable WebRTC?
|
||||||
|
// https://levelup.gitconnected.com/establishing-the-webrtc-connection-videochat-with-javascript-step-3-48d4ae0e9ea4
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._framerate = 2;
|
this._framerate = 2;
|
||||||
|
@ -14,7 +14,7 @@ import { RequireInteractMixin } from "./require-interact";
|
|||||||
import { FullyKioskMixin } from "./fullyKiosk";
|
import { FullyKioskMixin } from "./fullyKiosk";
|
||||||
import { BrowserModScreensaverMixin } from "./screensaver";
|
import { BrowserModScreensaverMixin } from "./screensaver";
|
||||||
import { BrowserModPopupsMixin } from "./popups";
|
import { BrowserModPopupsMixin } from "./popups";
|
||||||
import { BrowserModBrowserMixin } from "./browser";
|
import { BrowserStateMixin } from "./browser";
|
||||||
import pjson from "../../package.json";
|
import pjson from "../../package.json";
|
||||||
|
|
||||||
const ext = (baseClass, mixins) =>
|
const ext = (baseClass, mixins) =>
|
||||||
@ -28,9 +28,11 @@ const ext = (baseClass, mixins) =>
|
|||||||
// FullyKioskMixin,
|
// FullyKioskMixin,
|
||||||
// BrowserModMediaPlayerMixin,
|
// BrowserModMediaPlayerMixin,
|
||||||
// ]) {
|
// ]) {
|
||||||
export class BrowserMod extends CameraMixin(
|
export class BrowserMod extends BrowserStateMixin(
|
||||||
MediaPlayerMixin(
|
CameraMixin(
|
||||||
ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget)))
|
MediaPlayerMixin(
|
||||||
|
ScreenSaverMixin(RequireInteractMixin(ConnectionMixin(EventTarget)))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user