homebridge-arlo
Advanced tools
Comparing version 0.0.3 to 1.0.1-beta.0
131
index.js
'use strict'; | ||
const Arlo = require('node-arlo'); | ||
const EventEmitter = require('events').EventEmitter; | ||
const debug = require('debug')('Homebridge-Arlo'); | ||
const ArloCameraSource = require('./ArloCameraSource.js'); | ||
const DEFAULT_SUBSCRIBE_TIME = 60000; | ||
let Accessory, PlatformAccessory, Characteristic, Service, StreamController, UUIDGen; | ||
let Accessory, PlatformAccessory, Characteristic, Service, StreamController, UUIDGen, HAP; | ||
@@ -17,2 +18,3 @@ module.exports = function (homebridge) { | ||
UUIDGen = homebridge.hap.uuid; | ||
HAP = homebridge.hap; | ||
@@ -32,5 +34,13 @@ homebridge.registerPlatform('homebridge-arlo', 'Arlo', ArloPlatform, true); | ||
this.api = api; | ||
if (!api || api.version < 2.1) { throw new Error('Unexpected API version (less than 2.1)') } | ||
this.accessories = {}; | ||
this.log = log; | ||
this.config = this.config || {}; | ||
let include_cameras = true | ||
if (this.config.include_cameras === undefined) { | ||
this.config.include_cameras = true | ||
} | ||
this.setupListeners(); | ||
@@ -57,7 +67,8 @@ } | ||
} | ||
else if (deviceType === Arlo.CAMERA) { | ||
else if (deviceType === Arlo.CAMERA && this.config.include_cameras === true) { | ||
this.log("Found: Camera - %s [%s]", deviceName, device.id); | ||
let accessory = new PlatformAccessory(device.id, UUIDGen.generate(device.id), Accessory.Categories.CAMERA); | ||
let accessory = new PlatformAccessory(deviceName, UUIDGen.generate(device.id), Accessory.Categories.CAMERA); | ||
let service = accessory.getService(Service.AccessoryInformation); | ||
@@ -75,2 +86,3 @@ | ||
service = accessory.addService(Service.CameraControl, deviceName); | ||
service = accessory.addService(Service.Microphone, deviceName); | ||
@@ -86,3 +98,3 @@ service.addCharacteristic(Characteristic.NightVision); | ||
accessory.configureCameraSource(new ArloCameraSource(this.log, accessory, device)); | ||
accessory.configureCameraSource(new ArloCameraSource(this.log, accessory, device, HAP, this.config.streaming)); | ||
@@ -92,3 +104,3 @@ this.accessories[accessory.UUID] = new ArloCameraAccessory(this.log, accessory, device); | ||
} | ||
else if (deviceType === Arlo.Q) { | ||
else if (deviceType === Arlo.Q && this.config.include_cameras === true) { | ||
this.log("Found: Camera - %s [%s]", device.id, device.id); | ||
@@ -119,3 +131,3 @@ | ||
accessory.configureCameraSource(new ArloCameraSource(this.log, accessory, device)); | ||
accessory.configureCameraSource(new ArloCameraSource(this.log, accessory, device, HAP, this.config.streaming)); | ||
@@ -148,7 +160,7 @@ accessory.addService(Service.SecuritySystem, deviceName); | ||
} | ||
else if(device.getType() === Arlo.CAMERA) { | ||
else if(device.getType() === Arlo.CAMERA && this.config.include_cameras === true) { | ||
this.log("Online: Camera %s [%s]", accessory.displayName, device.id); | ||
this.accessories[uuid] = new ArloCameraAccessory(this.log, (accessory instanceof ArloCameraAccessory ? accessory.accessory : accessory), device); | ||
} | ||
else if(device.getType() === Arlo.Q) { | ||
else if(device.getType() === Arlo.Q && this.config.include_cameras === true) { | ||
this.log("Online: Camera %s [%s]", accessory.displayName, device.id); | ||
@@ -461,103 +473,2 @@ this.accessories[uuid] = new ArloQAccessory(this.log, this.config, (accessory instanceof ArloQAccessory ? accessory.accessory : accessory), device); | ||
class ArloCameraSource extends EventEmitter { | ||
constructor(log, accessory, device) { | ||
super(); | ||
this.log = log; | ||
this.accessory = accessory; | ||
this.device = device; | ||
this.services = []; | ||
this.streamControllers = []; | ||
this.lastSnapshot = null; | ||
let options = { | ||
proxy: false, // Requires RTP/RTCP MUX Proxy | ||
srtp: true, // Supports SRTP AES_CM_128_HMAC_SHA1_80 encryption | ||
video: { | ||
resolutions: [ | ||
[1280, 720, 30], | ||
[1280, 720, 15], | ||
[640, 360, 30], | ||
[640, 360, 15], | ||
[320, 240, 30], | ||
[320, 240, 15] | ||
], | ||
codec: { | ||
profiles: [StreamController.VideoCodecParamProfileIDTypes.MAIN], | ||
levels: [StreamController.VideoCodecParamLevelTypes.TYPE4_0] | ||
} | ||
}, | ||
audio: { | ||
codecs: [ | ||
{ | ||
type: 'OPUS', | ||
samplerate: 16 | ||
} | ||
] | ||
} | ||
} | ||
this._createStreamControllers(options); | ||
} | ||
handleCloseConnection(connectionID) { | ||
this.streamControllers.forEach(function(controller) { | ||
controller.handleCloseConnection(connectionID); | ||
}); | ||
} | ||
handleSnapshotRequest(request, callback) { | ||
let now = Date.now(); | ||
if (this.lastSnapshot && now < this.lastSnapshot + 300000) { | ||
this.log('Snapshot skipped: Camera %s [%s] - Next in %d secs', this.accessory.displayName, this.device.id, parseInt((this.lastSnapshot + 300000 - now) / 1000)); | ||
callback(); | ||
return; | ||
} | ||
this.log("Snapshot request: Camera %s [%s]", this.accessory.displayName, this.device.id); | ||
this.device.getSnapshot(function(error, data) { | ||
if (error) { | ||
this.log(error); | ||
callback(); | ||
return; | ||
} | ||
this.lastSnapshot = Date.now(); | ||
this.log("Snapshot confirmed: Camera %s [%s]", this.accessory.displayName, this.device.id); | ||
this.device.once(Arlo.FF_SNAPSHOT, function(url) { | ||
this.device.downloadSnapshot(url, function (data) { | ||
this.log("Snapshot downloaded: Camera %s [%s]", this.accessory.displayName, this.device.id); | ||
callback(undefined, data); | ||
}.bind(this)); | ||
}.bind(this)); | ||
}.bind(this)); | ||
} | ||
handleStreamRequest(request) { | ||
this.log("handleStreamRequest"); | ||
} | ||
prepareStream(request, callback) { | ||
this.log("prepareStream"); | ||
/* | ||
this.device.getStream(function(error, data, body) { | ||
this.log(body); | ||
callback(); | ||
}.bind(this)); | ||
*/ | ||
} | ||
_createStreamControllers(options) { | ||
//this.log("_createStreamControllers"); | ||
let streamController = new StreamController(1, options, this); | ||
this.services.push(streamController.service); | ||
this.streamControllers.push(streamController); | ||
} | ||
} | ||
class ArloQAccessory { | ||
@@ -564,0 +475,0 @@ constructor(log, config, accessory, device) { |
{ | ||
"displayName": "Homebridge Arlo", | ||
"name": "homebridge-arlo", | ||
"version": "0.0.3", | ||
"author": "David Parry <npm@introversion.com.au>", | ||
"description": "Arlo platform plugin for homebridge", | ||
"version": "1.0.1-beta.0", | ||
"description": "Arlo plugin for homebridge", | ||
"author": "homebridge-plugins", | ||
"main": "index.js", | ||
"scripts": { | ||
"developer": "DEBUG=honeywell-home homebridge -D -U `pwd` -P `pwd`", | ||
"lint": "jshint package.json *.js", | ||
"watch": "nodemon" | ||
}, | ||
"license": "ISC", | ||
"keywords": [ | ||
"homebridge-plugin", | ||
"arlo", | ||
"homebridge" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com/devbobo/homebridge-arlo.git" | ||
"url": "git://github.com/homebridge-plugins/homebridge-arlo.git" | ||
}, | ||
"bugs": { | ||
"url": "http://github.com/homebridge-plugins/homebridge-arlo/issues" | ||
}, | ||
"engines": { | ||
"node": ">=6", | ||
"homebridge": ">=0.4.29" | ||
"node": ">=8.15.1", | ||
"homebridge": ">=1.0.0" | ||
}, | ||
"keywords": [ | ||
"homebridge-plugin", | ||
"arlo" | ||
], | ||
"devDependencies": { | ||
"nodemon": ">=2.0.2" | ||
}, | ||
"dependencies": { | ||
"node-arlo": ">=0.0.7" | ||
"debug": "^4.1.1", | ||
"ffmpeg-for-homebridge": "0.0.5", | ||
"node-arlo": ">=0.0.7", | ||
"ip": "1.x" | ||
} | ||
} |
# homebridge-arlo | ||
[![npm package](https://nodei.co/npm/homebridge-arlo.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/homebridge-arlo/) | ||
[![npm package](https://badgen.net/npm/v/homebridge-arlo)](https://nodei.co/npm/homebridge-arlo/) | ||
[![donate](https://img.shields.io/badge/%24-Buy%20me%20a%20coffee-ff69b4.svg)](https://www.buymeacoffee.com/devbobo) | ||
[![Slack Channel](https://img.shields.io/badge/slack-homebridge--arlo-e01563.svg)](https://homebridgeteam.slack.com/messages/C5C0Z6XPW) | ||
Arlo platform plugin for [Homebridge](https://github.com/homebridge/homebridge). | ||
Arlo platform plugin for [Homebridge](https://github.com/nfarina/homebridge). | ||
# Installation | ||
@@ -34,3 +31,3 @@ | ||
```javascript | ||
```javascript | ||
"platforms": [ | ||
@@ -41,3 +38,3 @@ { | ||
"email": "<insert arlo account email address>", | ||
"password": "<insert arlo account password>" | ||
"password": "<insert arlo account password>", | ||
"interval": 6000 | ||
@@ -51,2 +48,3 @@ } | ||
## Optional parameters | ||
### Modes | ||
By default, Arlo only provides two modes (**armed** and **disarmed**). Since | ||
@@ -75,6 +73,74 @@ HomeKit allows a security system to have 4 states (**away**, **home**, | ||
### Streaming | ||
Live video streaming functionality requires transcoding of the video and audio streams provided by Arlo into a format acceptable to HomeKit. By default, this transcoding is assumed to be performed by a local installation of FFmpeg with the `libx264` video codec and `libfdk_aac` audio codec. Alternate configuration options are provided to help optimize the transcoding performance. | ||
- `videoProcessor`: The video processor used to perform transcoding. Defaults to `ffmpeg`. An alternate executable maybe used, however it needs to conform to ffmpeg parameters. | ||
- `videoDecoder`: The video codec used to decode the incoming h264 stream from the Arlo server. Defaults to no value, meaning the default h.264 software decoder (`libx264`) will typically be used. | ||
- `videoEncoder`: The video codec used to encode the outgoing h264 stream to the iOS client device. Defaults to `libx264`. | ||
- `audioEncoder`: The audio codec that will be used to decode/encode the audio stream. HomeKit requires either an Opus or AAC-ELD format audio stream. Defaults to the `libopus` codec, and currently Homebridge-Arlo tells HomeKit it only supports the Opus audio type. | ||
- `packetsize`: The packet sized to be used. Defaults to 1316. Use smaller multiples of 188 to possibly improve performance (376, 564, etc) | ||
- `maxBitrate`: The maximum bitrate of the encoded stream in kbit/s, the default is 300. | ||
- `additionalVideoCommands`: Any video-specific additional flags or commands to pass to the ffmpeg executable. | ||
- `additionalAudioCommands`: Any audio-specific additional flags or commands to pass to the ffmpeg executable. | ||
### Streaming with a Raspberry Pi 3 | ||
The Raspberry Pi 3 has both hardware decoder and encoder functionality, which can help with transcoding performance. However you will need to compile FFmpeg yourself to enable the hardware capability. | ||
Even if you unconcerned with hardware transcoding, you will likely need to compile FFmpeg with either the `Opus` or `libfdk_aac` encoders enabled in order to output the required Opus or AAC-ELD audio format. | ||
The below defines suggested compliation steps for FFmpeg on Raspberry Pi 3 that takes advantage of both the hardware encoder (omx) and decoder (mmal), and uses `libopus-dev` and/or `libfdk_aac` to enable transcoding of the audio. | ||
Note: This assumes you're using Raspbian Stretch. | ||
```bash | ||
# Go to home folder | ||
cd ~ | ||
# Install build tools | ||
sudo apt update | ||
sudo apt install build-essential pkg-config autoconf automake libtool checkinstall git | ||
# Install various dependencies | ||
sudo apt install libssl-dev libx264-dev libopus-dev libomxil-bellagio-dev | ||
# Clone libfdk-aac-dev | ||
git clone https://github.com/mstorsjo/fdk-aac.git | ||
cd fdk-aac | ||
# Configure and build libfdk-aac-dev | ||
./autogen.sh | ||
./configure --prefix=/usr/local --enable-shared --enable-static | ||
# Uses -j4 flag to use multiple cores during compilation | ||
make -j4 | ||
sudo make install | ||
sudo ldconfig | ||
cd .. | ||
# OPTIONAL: Remove any installed ffmpeg to avoid conflicts | ||
sudo apt remove ffmpeg | ||
# Clone ffmpeg | ||
git clone https://github.com/FFmpeg/FFmpeg.git | ||
cd FFmpeg | ||
# Configure ffmpeg | ||
./configure --prefix=/usr/local --arch=armel --target-os=linux --enable-openssl \ | ||
--enable-omx --enable-omx-rpi --enable-nonfree --enable-gpl --enable-libfdk-aac \ | ||
--enable-libopus --enable-mmal --enable-libx264 --enable-decoder=h264 --enable-network \ | ||
--enable-protocol=tcp --enable-demuxer=rtsp | ||
# Build ffmpeg | ||
sudo make -j4 | ||
# Install ffmpeg, and use checkinstall to build a self-contained deb file that can be easily | ||
# backed up for later use or reinstallation. Fill in all information requested by checkinstall. | ||
sudo checkinstall | ||
# Lock the custom ffmpeg package so it isn't replaced accidentally | ||
echo "ffmpeg hold" | sudo dpkg --set-selections | ||
``` | ||
Thanks to KhaosT for the base ffmpeg implementation and setup instructions in [homebridge-camera-ffmpeg](https://github.com/KhaosT/homebridge-camera-ffmpeg) and the [Maniacland Blog](https://maniaclander.blogspot.com/2017/08/ffmpeg-with-pi-hardware-acceleration.html)/[locutusofborg780](https://www.reddit.com/r/raspberry_pi/comments/5677qw/hardware_accelerated_x264_encoding_with_ffmpeg/) for FFmpeg configuration instructions. | ||
### Sample Configuration with Optional Parameters | ||
```javascript | ||
This sample configuration specifies that for streaming transcoding, ffmpeg should use the `h264_mmal` and `h264_omx` hardware decoders/encoders for the video stream. | ||
```javascript | ||
"platforms": [ | ||
@@ -88,3 +154,10 @@ { | ||
"night_arm": "mode3" | ||
"streaming": { | ||
"videoDecoder": "h264_mmal", | ||
"videoEncoder": "h264_omx", | ||
"packetSize": 564 | ||
} | ||
} | ||
} | ||
] | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
73400
15
1227
0
159
4
1
2
3
+ Addeddebug@^4.1.1
+ Addedffmpeg-for-homebridge@0.0.5
+ Addedip@1.x
+ Addedchownr@2.0.0(transitive)
+ Addeddecompress-response@4.2.1(transitive)
+ Addeddetect-libc@1.0.3(transitive)
+ Addeddotenv@8.6.0(transitive)
+ Addedffmpeg-for-homebridge@0.0.5(transitive)
+ Addedfs-minipass@2.1.0(transitive)
+ Addedip@1.1.9(transitive)
+ Addedmimic-response@2.1.0(transitive)
+ Addedminipass@3.3.65.0.0(transitive)
+ Addedminizlib@2.1.2(transitive)
+ Addedmkdirp@1.0.4(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedsimple-concat@1.0.1(transitive)
+ Addedsimple-get@3.1.1(transitive)
+ Addedtar@6.2.1(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedyallist@4.0.0(transitive)