homebridge-arlo
Advanced tools
Comparing version 0.0.1 to 0.0.2
329
index.js
@@ -40,3 +40,3 @@ 'use strict'; | ||
if (deviceType === "basestation") { | ||
if (deviceType === Arlo.BASESTATION) { | ||
this.log("Found: Base Station - %s [%s]", deviceName, device.id); | ||
@@ -54,4 +54,33 @@ | ||
} | ||
else if (deviceType === "camera") { | ||
// TODO | ||
else if (deviceType === Arlo.CAMERA) { | ||
this.log("Found: Camera - %s [%s]", deviceName, device.id); | ||
let accessory = new PlatformAccessory(deviceName, UUIDGen.generate(device.id), Accessory.Categories.CAMERA); | ||
let service = accessory.getService(Service.AccessoryInformation); | ||
service.setCharacteristic(Characteristic.Manufacturer, "Arlo") | ||
.setCharacteristic(Characteristic.Model, deviceModel); | ||
service.getCharacteristic(Characteristic.FirmwareRevision); | ||
service.getCharacteristic(Characteristic.HardwareRevision); | ||
accessory.addService(Service.BatteryService, deviceName); | ||
accessory.addService(Service.MotionSensor, deviceName); | ||
service = accessory.addService(Service.CameraControl, deviceName); | ||
service.addCharacteristic(Characteristic.NightVision); | ||
service.addCharacteristic(Characteristic.ImageMirroring); | ||
service.addCharacteristic(Characteristic.ImageRotation) | ||
.setProps({ | ||
maxValue: 180, | ||
minValue: 0, | ||
minStep: 180 | ||
}); | ||
accessory.configureCameraSource(new ArloCameraSource(this.log, accessory, device)); | ||
this.accessories[accessory.UUID] = new ArloCameraAccessory(this.log, accessory, device); | ||
this.api.publishCameraAccessories("homebridge-arlo", [accessory]); | ||
} | ||
@@ -68,3 +97,3 @@ } | ||
arlo.on("found", function(device) { | ||
arlo.on(Arlo.FOUND, function(device) { | ||
let uuid = UUIDGen.generate(device.id); | ||
@@ -76,8 +105,8 @@ let accessory = this.accessories[uuid]; | ||
} | ||
else if(device.getType() === "basestation") { | ||
else if(device.getType() === Arlo.BASESTATION) { | ||
this.log("Online: Base Station %s [%s]", accessory.displayName, device.id); | ||
this.accessories[uuid] = new ArloBaseStationAccessory(this.log, this.config, (accessory instanceof ArloBaseStationAccessory ? accessory.accessory : accessory), device); | ||
} | ||
else if(device.getType() === "camera") { | ||
// TODO | ||
else if(device.getType() === Arlo.CAMERA) { | ||
this.accessories[uuid] = new ArloCameraAccessory(this.log, (accessory instanceof ArloBaseStationAccessory ? accessory.accessory : accessory), device); | ||
} | ||
@@ -207,1 +236,287 @@ }.bind(this)); | ||
class ArloCameraAccessory { | ||
constructor(log, accessory, device) { | ||
this.accessory = accessory; | ||
this.device = device; | ||
this.log = log; | ||
this.accessory | ||
.getService(Service.AccessoryInformation) | ||
.setCharacteristic(Characteristic.Model, device.getModel()) | ||
.setCharacteristic(Characteristic.SerialNumber, device.getSerialNumber()); | ||
this.setupListeners(); | ||
this.device.get(); | ||
} | ||
setupListeners() { | ||
this.accessory | ||
.getService(Service.CameraControl) | ||
.getCharacteristic(Characteristic.On) | ||
.on('set', this.setPrivacyActive.bind(this)); | ||
this.accessory | ||
.getService(Service.CameraControl) | ||
.getCharacteristic(Characteristic.ImageMirroring) | ||
.on('set', this.setImageMirroring.bind(this)); | ||
this.accessory | ||
.getService(Service.CameraControl) | ||
.getCharacteristic(Characteristic.ImageRotation) | ||
.on('set', this.setImageRotation.bind(this)); | ||
setTimeout(function(){ | ||
this.device.get(); | ||
}.bind(this)); | ||
this.device.on(Arlo.BATTERY, this.updateBatteryLevel.bind(this)); | ||
this.device.on(Arlo.CHARGING, this.updateChargingState.bind(this)); | ||
this.device.on(Arlo.MOTION, this.updateMotionDetected.bind(this)); | ||
this.device.on(Arlo.UPDATE, function(info) { | ||
this.updateInfo(info); | ||
this.updateConnectionState(info.connectionState); | ||
this.updateImageMirroring(info.mirror); | ||
this.updateImageRotation(info.flip); | ||
this.updateNightVision(info.nightVisionMode); | ||
this.updatePrivacyActive(info.privacyActive); | ||
}.bind(this)); | ||
} | ||
setImageMirroring(value, callback) { | ||
this.device.set({mirror: value}, function() { | ||
callback(null); | ||
}) | ||
} | ||
setImageRotation(value, callback) { | ||
this.device.set({flip: (value > 0 ? true : false)}, function() { | ||
callback(null); | ||
}) | ||
} | ||
setPrivacyActive(value, callback) { | ||
this.device.set({privacyActive: value == false}, function() { | ||
callback(null); | ||
}) | ||
} | ||
updateInfo(info) { | ||
if (info === undefined) { | ||
return; | ||
} | ||
let service = this.accessory.getService(Service.AccessoryInformation); | ||
if (info.modelId) { | ||
service.getCharacteristic(Characteristic.Model).updateValue(info.modelId); | ||
} | ||
if (info.serialNumber) { | ||
service.getCharacteristic(Characteristic.SerialNumber).updateValue(info.serialNumber); | ||
} | ||
if (info.swVersion) { | ||
service.getCharacteristic(Characteristic.FirmwareRevision).updateValue(info.swVersion); | ||
} | ||
if (info.hwVersion) { | ||
service.getCharacteristic(Characteristic.HardwareRevision).updateValue(info.hwVersion); | ||
} | ||
} | ||
updateBatteryLevel(batteryLevel) { | ||
if (batteryLevel === undefined) { | ||
return; | ||
} | ||
this.accessory | ||
.getService(Service.BatteryService) | ||
.getCharacteristic(Characteristic.BatteryLevel) | ||
.updateValue(batteryLevel); | ||
} | ||
updateChargingState(value) { | ||
let state = Characteristic.ChargingState.NOT_CHARGEABLE; | ||
if (value !== undefined) { | ||
state = value != 'Off' ? Characteristic.ChargingState.CHARGING : Characteristic.ChargingState.NOT_CHARGING; | ||
} | ||
this.accessory | ||
.getService(Service.BatteryService) | ||
.getCharacteristic(Characteristic.ChargingState) | ||
.updateValue(state); | ||
} | ||
updateConnectionState(connectionState) { | ||
if (connectionState === undefined) { | ||
return; | ||
} | ||
let online = connectionState === 'available'; | ||
this.log("%s: Camera %s [%s]", (online ? 'Online' : 'Offline'), this.accessory.displayName, this.device.id); | ||
} | ||
updateMotionDetected(motionDetected) { | ||
if (motionDetected === undefined) { | ||
return; | ||
} | ||
this.accessory | ||
.getService(Service.MotionSensor) | ||
.getCharacteristic(Characteristic.MotionDetected) | ||
.updateValue(motionDetected); | ||
} | ||
updateImageMirroring(mirror) { | ||
if (mirror === undefined) { | ||
return; | ||
} | ||
this.accessory | ||
.getService(Service.CameraControl) | ||
.getCharacteristic(Characteristic.ImageMirroring) | ||
.updateValue(mirror); | ||
} | ||
updateImageRotation(flip) { | ||
if (flip === undefined) { | ||
return; | ||
} | ||
this.accessory | ||
.getService(Service.CameraControl) | ||
.getCharacteristic(Characteristic.ImageRotation) | ||
.updateValue(flip === true ? 180 : 0); | ||
} | ||
updateNightVision(nightVisionMode) { | ||
if (nightVisionMode === undefined) { | ||
return; | ||
} | ||
this.accessory | ||
.getService(Service.CameraControl) | ||
.getCharacteristic(Characteristic.NightVision) | ||
.updateValue(nightVisionMode === 1); | ||
} | ||
updatePrivacyActive(privacyActive) { | ||
if (privacyActive === undefined) { | ||
return; | ||
} | ||
this.accessory | ||
.getService(Service.CameraControl) | ||
.getCharacteristic(Characteristic.On) | ||
.updateValue(privacyActive == false); | ||
} | ||
} | ||
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) { | ||
// todo - remove this | ||
let now = Date.now(); | ||
if (this.lastSnapshot && now < this.lastSnapshot + 300000) { | ||
this.log('Camera %s [%s] - Next snapshot in %d secs', this.accessory.displayName, this.device.id, parseInt((this.lastSnapshot + 300000 - now) / 1000)); | ||
callback(); | ||
return; | ||
} | ||
this.log("Camera %s [%s] - Snapshot request", this.accessory.displayName, this.device.id); | ||
//this.once('snapshot', function(data) { | ||
// callback(undefined,data); | ||
//}); | ||
this.device.getSnapshot(function(error, data) { | ||
if (error) { | ||
this.log(error); | ||
callback(); | ||
return; | ||
} | ||
this.lastSnapshot = Date.now(); | ||
this.log("Camera %s [%s] - Snapshot confirmed", this.accessory.displayName, this.device.id); | ||
this.log(data); | ||
this.device.once('fullFrameSnapshotAvailable', function(url) { | ||
this.device.downloadSnapshot(url, function (data) { | ||
this.log("Camera %s [%s] - Snapshot downloaded", 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); | ||
} | ||
} |
{ | ||
"name": "homebridge-arlo", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"author": "David Parry <npm@introversion.com.au>", | ||
@@ -11,4 +11,4 @@ "description": "Arlo platform plugin for homebridge", | ||
"engines": { | ||
"node": ">=6.0.0", | ||
"homebridge": ">=0.4.1" | ||
"node": ">=6", | ||
"homebridge": ">=0.4.29" | ||
}, | ||
@@ -20,4 +20,4 @@ "keywords": [ | ||
"dependencies": { | ||
"node-arlo": ">=0.0.1" | ||
"node-arlo": ">=0.0.5" | ||
} | ||
} |
# homebridge-arlo | ||
[![npm package](https://nodei.co/npm/homebridge-arlo.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/homebridge-arlo/) | ||
[![NPM Version](https://img.shields.io/npm/v/homebridge-arlo.svg)](https://www.npmjs.com/package/homebridge-arlo) | ||
[![Dependency Status](https://img.shields.io/versioneye/d/nodejs/arlo.svg)](https://www.versioneye.com/nodejs/homebridge-arlo/) | ||
[![Slack Channel](https://img.shields.io/badge/slack-homebridge--arlo-e01563.svg)](https://homebridgeteam.slack.com/messages/C5C0Z6XPW) | ||
@@ -5,0 +9,0 @@ Arlo platform plugin for [Homebridge](https://github.com/nfarina/homebridge). |
21149
422
80
Updatednode-arlo@>=0.0.5