homebridge-website-to-camera
Advanced tools
Comparing version 1.7.1 to 2.0.0
"use strict"; | ||
const packageJSON = require("./package.json"); | ||
const CameraSource = require("./CameraSource"); | ||
const CameraServer = require("./CameraServer"); | ||
const {Categories, Service, Characteristic} = require("hap-nodejs"); | ||
const ScreenshotHelper = require("./ScreenshotHelper"); | ||
@@ -20,17 +23,34 @@ module.exports = (hap, Accessory, log) => class CameraAccessory extends Accessory { | ||
var uuid = hap.uuid.generate("homebridge-website-to-camera:" + id); | ||
super(name, uuid, hap.Accessory.Categories.CAMERA); | ||
this.getService(hap.Service.AccessoryInformation) | ||
.setCharacteristic(hap.Characteristic.Manufacturer, "David") | ||
.setCharacteristic(hap.Characteristic.Model, "Website") | ||
.setCharacteristic(hap.Characteristic.SerialNumber, id) | ||
.setCharacteristic(hap.Characteristic.FirmwareRevision, packageJSON.version); | ||
this.on("identify", function (paired, callback) { | ||
log("identify"); | ||
if (!!callback) { | ||
callback(); | ||
} | ||
}); | ||
var cameraSource = new CameraSource(hap, conf, log); | ||
this.configureCameraSource(cameraSource); | ||
if (conf.live === "true") { | ||
super(name, uuid, Categories.OTHER); | ||
this.getService(Service.AccessoryInformation) | ||
.setCharacteristic(Characteristic.Manufacturer, "David") | ||
.setCharacteristic(Characteristic.Model, "Website") | ||
.setCharacteristic(Characteristic.SerialNumber, id) | ||
.setCharacteristic(Characteristic.FirmwareRevision, packageJSON.version); | ||
this.on("identify", function (paired, callback) { | ||
log("identify"); | ||
if (!!callback) { | ||
callback(); | ||
} | ||
}); | ||
this.screenshotHelper = new ScreenshotHelper(log, conf.url, conf.chromiumPath, conf.ignoreHTTPSErrors, conf.jsFile); | ||
new CameraServer(hap, conf, log); | ||
} else { | ||
super(name, uuid, Categories.CAMERA); | ||
this.getService(Service.AccessoryInformation) | ||
.setCharacteristic(Characteristic.Manufacturer, "David") | ||
.setCharacteristic(Characteristic.Model, "Website") | ||
.setCharacteristic(Characteristic.SerialNumber, id) | ||
.setCharacteristic(Characteristic.FirmwareRevision, packageJSON.version); | ||
this.on("identify", function (paired, callback) { | ||
log("identify"); | ||
if (!!callback) { | ||
callback(); | ||
} | ||
}); | ||
const cameraSource = new CameraSource(hap, conf, log); | ||
this.configureCameraSource(cameraSource); | ||
} | ||
} | ||
}; |
@@ -15,3 +15,3 @@ "use strict"; | ||
this.streamControllers = []; | ||
this.screenshotHelper = new ScreenshotHelper(log, conf.url, conf.chromiumPath, conf.ignoreHTTPSErrors); | ||
this.screenshotHelper = new ScreenshotHelper(log, conf.url, conf.chromiumPath, conf.ignoreHTTPSErrors, conf.jsFile); | ||
@@ -18,0 +18,0 @@ this.pendingSessions = {}; |
@@ -52,2 +52,26 @@ { | ||
"type": "string" | ||
}, | ||
"cacheTime": { | ||
"title": "Cache Time", | ||
"type": "integer" | ||
}, | ||
"live": { | ||
"title": "Live mode", | ||
"type": "string" | ||
}, | ||
"liveSnapshotInterval": { | ||
"title": "Snapshot interval (Live mode)", | ||
"type": "integer" | ||
}, | ||
"liveRefreshInterval": { | ||
"title": "Page refresh interval (Live mode)", | ||
"type": "integer" | ||
}, | ||
"livePort": { | ||
"title": "Port for Live-Stream", | ||
"type": "integer" | ||
}, | ||
"jsFile": { | ||
"title": "Path to custom JS file", | ||
"type": "string" | ||
} | ||
@@ -54,0 +78,0 @@ } |
{ | ||
"name": "homebridge-website-to-camera", | ||
"version": "1.7.1", | ||
"version": "2.0.0", | ||
"description": "shows the screenshot of a website as camera (image)", | ||
@@ -43,5 +43,10 @@ "main": "index.js", | ||
"ip": "^1.1.5", | ||
"mjpeg-server": "^0.3.1", | ||
"puppeteer-core": "^5.5.0", | ||
"username": "^5.1.0" | ||
}, | ||
"devDependencies": { | ||
"hap-nodejs": "^0.9.7", | ||
"homebridge": "^1.3.4" | ||
} | ||
} |
@@ -5,3 +5,5 @@ # homebridge-website-to-camera | ||
[![NPM version](https://badge.fury.io/js/homebridge-website-to-camera.svg)](https://npmjs.org/package/homebridge-website-to-camera) [![Dependency Status](https://david-dm.org/werthdavid/homebridge-website-to-camera.svg)](https://david-dm.org/werthdavid/homebridge-website-to-camera) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) [![Downloads](https://img.shields.io/npm/dm/homebridge-website-to-camera.svg)](https://npmjs.org/package/homebridge-website-to-camera) | ||
[![NPM version](https://badge.fury.io/js/homebridge-website-to-camera.svg)](https://npmjs.org/package/homebridge-website-to-camera) | ||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) | ||
[![Downloads](https://img.shields.io/npm/dm/homebridge-website-to-camera.svg)](https://npmjs.org/package/homebridge-website-to-camera) | ||
@@ -19,3 +21,3 @@ | ||
Make sure you have homebridge installed. | ||
Make sure you have Homebridge installed. | ||
@@ -38,3 +40,3 @@ 1. Download the latest Chromium `sudo apt-get install chromium-browser` | ||
"name": "Website 1", | ||
"url": "http://github.com", | ||
"url": "https://github.com", | ||
"chromiumPath": "/usr/bin/chromium-browser" | ||
@@ -60,3 +62,3 @@ } | ||
* `url` the URL of the website that is to be captured (required) | ||
* `scale` HomeApp requests an (probably device dependent) size for the preview-image. | ||
* `scale` HomeApp requests a (device dependent) size for the preview-image. | ||
With e.g. `scale` set to `2` (default) the virtual browser window is set to this size. Best is to skip this field. | ||
@@ -69,2 +71,7 @@ * OR `width` / `height` the width/height of the virtual browser window. This is optional and overrides `scale`. | ||
* `cacheTime` will cache the image for the given time (in seconds) and a new screenshot will be created only if the time since the last screenshot is bigger than the given time | ||
* `jsFile` specify the full path of a custom JS-file that will be injected to the page (e.g. if you want to hide cookie-banners) | ||
* `live` enabled live mode to use with ffmpeg plugin (defaults to `false`). See below! | ||
* `liveSnapshotInterval` takes a screenshot of the website in the specified interval (in milliseconds). 1000 is a good value to start with. | ||
* `liveRefreshInterval` how often the page should be reloaded in live-mode (in milliseconds). If live mode is not enabled the page will be opened every time a screenshot is taken so refreshing is not needed. | ||
* `livePort` when `live` is set to true, this will be the port where the live server is running (defaults to 8554). make sure you use a free port especially if you have multiple cameras. | ||
@@ -78,9 +85,56 @@ # Usage | ||
* Select Camera | ||
* Provide the code from Homebridge startup output (as stated in your config.json) | ||
# AppleTV / Live-Stream :tada: | ||
In Homebridge a Camera-Accessory provides two functionalities: | ||
- deliver a "still image" that serves as a preview for the camera | ||
- deliver a live-stream if the camera is "clicked" | ||
In previous versions of the plugin, only the still image was supported. | ||
This is a problem for AppleTV as this device does not show the still image, instead it directly shows the live-stream. | ||
Implementing the live-stream is not that easy with Node.js (Homekit needs the Camera to connect to the client via SRTP | ||
and send the video e.g. with x264). Instead of implementing this directly this plugin implements a helper, that lets | ||
you enable live-streaming via the [homebridge-camera-ffmpeg](https://github.com/Sunoo/homebridge-camera-ffmpeg) plugin. | ||
## Enable Live-Stream | ||
:exclamation: You need to install [homebridge-camera-ffmpeg](https://github.com/Sunoo/homebridge-camera-ffmpeg) as well! | ||
```json | ||
"platforms": [ | ||
{ | ||
"platform": "website-camera", | ||
"cameras": [ | ||
{ | ||
"name": "Website 1", | ||
"url": "https://github.com", | ||
"live": "true" | ||
"liveSnapshotInterval": 1000, | ||
"liveRefreshInterval": 5000, | ||
"livePort": 8554 | ||
} | ||
] | ||
}, | ||
{ | ||
"platform": "Camera-ffmpeg", | ||
"cameras": [ | ||
{ | ||
"name": "Website Camera", | ||
"videoConfig": { | ||
"source": "-i http://localhost:8554", | ||
"stillImageSource": "-i http://localhost:8554/still" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
``` | ||
:grey_exclamation: if `live` is enabled, the website-to-camera plugin will not expose a camera on its own! | ||
There will only be a server listening on the given port that offers a MJPEG live-stream. | ||
# Background | ||
The plugin uses Puppeteer/Chrome headless to capture the screenshots. The Browser instance stays open all the time for better performance and less CPU/Mem consumption. | ||
# TODO | ||
* Live-Video not working. As far as I understood HomeKit requires an RTSP-stream where it can connect to. So this feature might not come at all. |
@@ -1,7 +0,8 @@ | ||
const puppeteer = require('puppeteer-core'); | ||
const username = require('username'); | ||
const puppeteer = require("puppeteer-core"); | ||
const username = require("username"); | ||
const fs = require("fs"); | ||
module.exports = ScreenshotHelper; | ||
function ScreenshotHelper(log, url, chromiumPath = "/usr/bin/chromium-browser", ignoreHTTPSErrors = false) { | ||
function ScreenshotHelper(log, url, chromiumPath = "/usr/bin/chromium-browser", ignoreHTTPSErrors = false, jsFile = undefined) { | ||
this.log = log; | ||
@@ -12,2 +13,3 @@ this.url = url; | ||
this.log("Initialized ScreenshotHelper"); | ||
this.jsFile = jsFile; | ||
} | ||
@@ -20,2 +22,9 @@ | ||
ScreenshotHelper.prototype.getScreenshot = async function (width, height, networkTimeout, renderTimeout) { | ||
const page = await this.getPage(width, height, networkTimeout, renderTimeout); | ||
const screenshot = await this.makeScreenshot(page) | ||
page.close(); | ||
return screenshot; | ||
}; | ||
ScreenshotHelper.prototype.getPage = async function (width, height, networkTimeout, renderTimeout) { | ||
if (!this.browser) { | ||
@@ -29,3 +38,3 @@ this.log("Starting new instance of Chromium: " + this.chromiumPath); | ||
ignoreHTTPSErrors: this.ignoreHTTPSErrors, | ||
args: isRoot ? ['--no-sandbox'] : [] | ||
args: isRoot ? ["--no-sandbox"] : [] | ||
} | ||
@@ -40,9 +49,26 @@ ); | ||
this.log("Going to page: " + this.url); | ||
await page.goto(this.url, {waitUntil: 'networkidle2', timeout: networkTimeout}); | ||
await page.goto(this.url, {waitUntil: "networkidle2", timeout: networkTimeout}); | ||
this.log("Loading finished, waiting " + renderTimeout + "ms before taking screenshot"); | ||
await this.sleep(renderTimeout); | ||
if (!!this.jsFile) { | ||
await this.addJs(page, this.jsFile); | ||
} | ||
return page; | ||
}; | ||
ScreenshotHelper.prototype.makeScreenshot = async function (page, doLog = true) { | ||
const screenshot = await page.screenshot({type: "jpeg"}); | ||
this.log("Created screenshot"); | ||
page.close(); | ||
if (doLog) { | ||
this.log("Created screenshot"); | ||
} | ||
return screenshot; | ||
}; | ||
ScreenshotHelper.prototype.refresh = async function (page, networkTimeout) { | ||
await page.reload({waitUntil: "networkidle2", timeout: networkTimeout}); | ||
}; | ||
ScreenshotHelper.prototype.addJs = async function (page, jsFile) { | ||
const file = fs.readFileSync(jsFile, "utf8"); | ||
await page.addScriptTag({ content: file }); | ||
}; |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
1822846
12
482
135
4
2
1
1
+ Addedmjpeg-server@^0.3.1
+ Addedmjpeg-server@0.3.1(transitive)