pimatic-foscam
Version:
A plugin to display foscam HD cameras in the UI. Also provides an action to take snapshots.
247 lines (193 loc) • 7.08 kB
text/coffeescript
module.exports = (env) ->
Promise = env.require 'bluebird'
assert = env.require 'cassert'
request = env.require 'request'
fs = env.require 'fs'
path = env.require 'path'
#Stream = env.require 'node-rtsp-stream'
Stream = require(__dirname + '/lib/videoStream.js');
{parseString} = env.require 'xml2js'
M = env.matcher
_ = env.require('lodash')
class Foscam extends env.plugins.Plugin
init: (app, , ) =>
deviceConfigDef = require("./device-config-schema")
.ruleManager.addActionProvider(new FoscamActionProvider())
.deviceManager.registerDeviceClass("IpCamera", {
configDef: deviceConfigDef.IpCamera,
createCallback: (config) =>
camera = new IpCamera(, config)
return camera
})
.on "after init", =>
mobileFrontend = .pluginManager.getPlugin 'mobile-frontend'
if mobileFrontend?
mobileFrontend.registerAssetFile 'js', "pimatic-foscam/app/jsmpg.js"
mobileFrontend.registerAssetFile 'js', "pimatic-foscam/app/ipcamera-page.coffee"
mobileFrontend.registerAssetFile 'html', "pimatic-foscam/app/ipcamera-template.html"
else
env.logger.warn "pimatic-foscam could not find the mobile-frontend. No gui will be available"
class IpCamera extends env.devices.Device
attributes:
username:
description: "User name for the access"
type: "string"
default: ""
password:
description: "password for the access"
type: "string"
default: ""
cameraIp:
description: "IP of Camera"
type: "string"
default: ""
cameraPort:
description: "Port of Camera"
type: "number"
default : 88
actions:
saveSnapshot:
description: "Command to save the current image"
toggleInfrared:
description: "Command to toggle the IR mode"
template: "ipcamera"
self: null
constructor: (framework, ) ->
= .id
= .name
= .username
= .password
= .cameraIp
= .cameraPort
= .wsPort
= framework.io
self = @
= "http://" + + ":" + +
"/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=" +
+ "&pwd=" +
= 'rtsp://' + + ':' + + '@' +
+ '/videoSub'
stream = new Stream({
name: ,
streamUrl: ,
width: 320,
height: 180,
wsPort:
})
env.logger.info('Started WS stream at port ' + + ' from origin ' + )
super()
getCGIResponse: (command, cb) ->
cgiUrl = 'http://' + + ':' + +
'/cgi-bin/CGIProxy.fcgi?cmd=' + command +
'&usr=' + + '&pwd=' +
request cgiUrl, (err, res, body) ->
if err?.length
parseString body, (err, parsedObj) ->
cb? parsedObj.CGI_Result
else
cb? null
getImgPath: ->
imgPath = ""
if process.platform in ['win32', 'win64']
imgPath = path.dirname(fs.realpathSync(__filename+"\\..\\"))+"\\pimatic-mobile-frontend\\public\\foscam\\"
else
imgPath = path.dirname(fs.realpathSync(__filename+"/../"))+"/pimatic-mobile-frontend/public/foscam/"
return imgPath
createImgDirectory: ->
=
fs.exists(,(exists)=>
if !exists
fs.mkdir(,(stat)=>
console.log("Create directory " + )
)
)
getDateTime: ->
date = new Date();
hour = date.getHours();
hour = (hour < 10 ? "0" : "") + hour;
min = date.getMinutes();
min = (min < 10 ? "0" : "") + min;
sec = date.getSeconds();
sec = (sec < 10 ? "0" : "") + sec;
year = date.getFullYear();
month = date.getMonth() + 1;
month = (month < 10 ? "0" : "") + month;
day = date.getDate();
day = (day < 10 ? "0" : "") + day;
return year + month + day + "_" + hour + min + sec;
saveSnapshot: =>
filename = + '_snapshot.jpg'
req = request().pipe(fs.createWriteStream( + filename))
setTimeout =>
fs.createReadStream( + filename).pipe(fs.createWriteStream( + 'last_snapshot.jpg'))
, 500
env.logger.info('Snapshot from ' + + 'stored at ' + + filename)
.emit("snapshotSaved", filename)
return
toggleInfrared: =>
'setInfraLedConfig&mode=1'
if
'closeInfraLed'
= false
env.logger.info('Infrared off')
else
'openInfraLed'
= true
env.logger.info('Infrared on')
setTimeout =>
'setInfraLedConfig&mode=0'
, 60000
return
getUsername : -> Promise.resolve()
getPassword : -> Promise.resolve()
getCameraIp : -> Promise.resolve()
getCameraPort : -> Promise.resolve()
class FoscamActionProvider extends env.actions.ActionProvider
constructor: (, ) ->
parseAction: (input, context) =>
device = null
match = null
cameras = _(.deviceManager.devices).values().filter(
(device) => device.hasAction("saveSnapshot")
).value()
if cameras.length is 0
env.logger.info "no cameras with saveSnapshot action found"
return
m = M(input, context)
.match("take a", optional: yes)
.match("snapshot of ")
.matchDevice(cameras, (next, d) =>
if device? and device.id isnt d.id
context?.addError(""""#{input.trim()}" is ambiguous.""")
return
device = d
)
if m.hadMatch()
match = m.getFullMatch()
return {
token: match
nextInput: input.substring(match.length)
actionHandler: new FoscamActionHandler(, , device)
}
else
return null
class FoscamActionHandler extends env.actions.ActionHandler
constructor: (, , ) ->
assert ?
executeAction: (simulate) =>
if simulate
return Promise.resolve(__("would log 42"))
else
.saveSnapshot()
return Promise.resolve(__("logged 42"))
foscam = new Foscam
return foscam