node-enocean
Version:
full implementation of the enocean protocol (ESP3 + EEP2.6)
264 lines (246 loc) • 11.4 kB
JavaScript
// This file is part of node-enocean.
// node-enocean. is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// node-enocean. is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with node-enocean. If not, see <http://www.gnu.org/licenses/>.
// # memory implementation.
// this module implements saving and deleting sensors as well as hanbling telegrams from known sensor
// this implementation uses the fiesystem to stor sensor info
var path = require("path")
var fs = require("fs")
var level = require("level")
var knownSensors = ""
var db = null
module.exports = function(app,config){
this.timerId=null
if( config == undefined) config = {} // check if this was called with a config or not
// if a path to the memory dir was provided use that, otherwise use the default
var memoryDir = config.memoryDirectoryPath || path.join(__dirname,"../memory")
db = level(memoryDir,{valueEncoding:'json'})
app.db=db
// if a path to the sensorFile was provided use that, otherwise use the default
var outFile = config.sensorFilePath || path.join(__dirname + '/knownSensors.json')
if(!fs.existsSync(outFile)){
fs.writeFileSync(outFile,"{}")
}
knownSensors = require( outFile ) // load the sensorFile
app.learnMode = "off"
app.forgetMode = "off"
app.on( "data" , function( data ) {
if ( knownSensors.hasOwnProperty( data.senderId )) {
//if sensor is known, extract the content of the Data Bits.
// if(data.choice==="d2"){
// data.sensor = knownSensors[ data.senderId ]
// data.values = app.getData( data.sensor.eep , data.raw )
// knownSensors[ data.senderId ].last = data.values
// app.emitters.forEach(function(emitter){
// emitter.emit("known-data",data) // and emmit an event propagating the extracted Data downstream
// } )
// fs.writeFile( outFile , JSON.stringify( knownSensors , null , 4 ) , function( err ) {} )
// }
if( data.learnBit === 1 || data.choice === "f6" || data.choice === "d1") {
// but only if it is not a learn Telegram (learnBit==1)
var sensor = knownSensors[ data.senderId ] // get the sensor Info like the eep and manufacurer Info from the memory file
data.sensor = sensor // attach that info to the telegram data
data.values = app.getData( sensor.eep , data.raw ) // actually extract the Data
try{
data.data = app.getData2( sensor.eep , data.rawByte ) // actually extract the Data
}catch(err){
console.log("not supported")
}
db.put(data.senderId ,data,function(err){}) // store this Telegram in memory
app.emitters.forEach(function(emitter){
emitter.emit("known-data",data) // and emmit an event propagating the extracted Data downstream
} )
//fs.writeFile( outFile , JSON.stringify( knownSensors , null , 4 ) , function( err ) {} ) // and save it to disk
} else {
// if it is a learn telegram, check if we are in "teach in"-mode
if( app.learnMode === "on" ) {
// we are in teach in mode
// And we have just received a "tech in" telegram...
// but we allready know this sensor
app.learnMode = "off" // turn of "teach in"-mode to prevent unwanted sensors to be tought in accedently
app.emitters.forEach( function( emitter ) {
emitter.emit( "learn-mode-stop" , { code : 1 , reason : "already known sensor" } ) // tell everyone that we stop the "teach-in"-mode
} )
}
if( app.forgetMode === "on" ) {
// we are in forget mode, and we received a "teach-in" telegram of a known sensor
// this indicates that it should be forgotten.
app.forget( data.senderId ) // delete the sensor
}
}
if( app.forgetMode === "on" && data.choice === "f6" ) {
// we are in forget mode, and we received an RPS telegram form a known sensor
// but the learnBit is not set, as RPS telegramns don't have learnBits...
// this indicates that it should be forgotten.
app.forget( data.senderId ) // delete the sensor
}
} else {
// ???? we don't know this sensor ???
if( data.learnBit === 0 ) {
// but it's a "teach in"-telegram, so it wants to tell us about itself
if( app.learnMode === "on" ) {
// we are in learnMode, so extract the sensor info frm the telegram
// and save that info
if(data.choice==="d5") data.eep="d5-00-01" //its a 1BS Telegram there is currently only 1 defined so assume d5-00-01
app.learn( {
id : data.senderId,
eep : data.eep,
manufacturer : data.manufacturer,
title : "New " + app.eepDesc[data.eep.substring(0,5)], // give it some name
desc : "I'm a new sensor...", // and some description
eepFunc : app.eepDesc[data.eep.substring(0,5)], // finde the func description of the eep
eepType : app.eepDesc[data.eep] // find the Type description of the eep
} )
} else {
// we are not in teach in mode, but this is a "tech in" telegram
if(data.choice !== "f6" ) {
// "RPS" telegrams do not have a lernBit. depending on the Data Byte sometimes the Bit used for indicating "teach-in"-telegrams is set
// prevent false positives
app.emitters.forEach( function( emitter ) {
emitter.emit( "unknown-teach-in" , data ) // tell everyone we received an unknown "teach-in"
} )
}
}
} else {
// we don't know the sender and the leranBit is not set
if( data.choice === "f6" && app.learnMode === "on" ) {
// but this is an "RPS" signal ( remeber RPS don't have learn bits ), and we are in teach in mode
// so treat every RPS received during teach in a a request to be tought in
// do so...
app.learn( {
id : data.senderId,
eep : "f6-02-03",
manufacturer : "unknown",
title : "New RPS Switch", // give it some name
desc : "I'm a new sensor...", // and some description
eepFunc : app.eepDesc["f6-02"], // finde the func description of the eep
eepType : app.eepDesc["f6-02-03"] // find the Type description of the eep
} )
} else {
// we are not in learnMode and the sensor of this telegram is not known.
// neither is this a learn telegram.
app.emitters.forEach( function( emitter ){
emitter.emit( "unknown-data" , data ) // just tell everyone we received something, but we don't know what to do with it
} )
}
}
}
})
app.startLearning = function( ) {
// start learnMode ("tech-in"-mode)
// the learn mode is here to automaticly learn sensors that send a teach in telegram
app.learnMode = "on"
app.emitters.forEach( function( emitter ) {
emitter.emit( "learn-mode-start" , { timeout : app.timeout } ) // propagete that we are ready to learn
} )
this.timerId=setTimeout( app.stopLearning , app.timeout * 1000 ) // make sure we stop learning after timeout
}
app.stopLearning = function( ) {
// stop learnMode
if( app.learnMode == "on" ) {
// but only if we are still in leranMode
app.learnMode =" off"
clearTimeout(this.timerId)
app.emitters.forEach( function( emitter ) {
emitter.emit( "learn-mode-stop" , { code : 2 , reason : "timeout" } ) // tell everyone we are not in teach in anymore
} )
}
}
app.startForgetting = function( ) {
// start the forget mode
// this is used to delete single sensors, through its teach in telegram.
app.forgetMode = "on"
app.emitters.forEach( function( emitter ) {
emitter.emit( "forget-mode-start" , { timeout : app.timeout } ) // tell everyone we are in forget-mode
} )
this.timerId=setTimeout( app.stopForgetting , app.timeout * 1000 ) // make sure we leave stop mode after timeout
}
app.stopForgetting = function( ) {
// stop forget Mode
if( app.forgetMode == "on" ) {
// but only if we are in forget Mode
app.forgetMode = "off"
clearTimeout(this.timerId)
app.emitters.forEach( function( emitter ) {
emitter.emit( "forget-mode-stop" , { code : 2 , reason : "timeout" } ) // tell everyone we are not in forget mode anymore
} )
}
}
app.learn = function( sensor ) {
// actually learn a sensor.
// this function can be call from anywhwere.
// the sensor object should have the following fileds: id,eep,manufacturer,title,desc
// this can be used to update sensor info like desc and title...
knownSensors[ sensor.id ] = sensor // save the sensor under its id
app.learnMode = "off" // stop the learnMode in any case
clearTimeout(this.timerId)
app.emitters.forEach( function( emitter ) {
emitter.emit( "learn-mode-stop" , { code : 0 , reason : "success" } ) // tell everyone we are not in learn mode anymore
} )
fs.writeFile( outFile , JSON.stringify( knownSensors , null , 4 ) , function( err ) {
// and actually save it to disc
if( err ) {
app.emitters.forEach( function( emitter ) {
emitter.emit( "learn-error" , { err : err, reason: "error saving sensor file to disk" } ) // there was an error saving the file
} )
} else {
// the file was successfully saved
app.emitters.forEach( function( emitter ) {
emitter.emit( "learned" , sensor ) // let's tell everyone we where successfull attach the sensor info of the sensor we just saved
} )
}
})
}
app.forget = function( id ) {
// actuall delete a sensor by its id
var tmp = ""
if( knownSensors.hasOwnProperty( id ) ) {
tmp = knownSensors[ id ] // but befor we delete it, save the snsor info in a temporary variable
delete knownSensors[ id ] // delete the sensor from the knownSensor object (in memory)
}
app.forgetMode="off" // stop forget Mode
clearTimeout(this.timerId)
app.emitters.forEach( function( emitter ) {
emitter.emit( "forget-mode-stop" , { code : 0 , reason:"success"} ) // and tell the "world" we stoped forget mode
} )
fs.writeFile( outFile , JSON.stringify( knownSensors, null, 4), function( err ) {
// actually save the changes to disk
if(err) {
app.emitters.forEach( function( emitter ) {
emitter.emit( "forget-error" , { err : err, reason: "error saving sensor file to disk" } ) // there was an error saving the file
} )
} else {
app.emitters.forEach(function(emitter){
emitter.emit("forgotten",tmp) // let's tell everyone we where successfull, attach the sensor info of the just deleted sensor
})
}
} )
}
app.info = function ( id ) {
// get info of a specific sensor
var sensor = knownSensors[ id ]
return sensor
}
app.getLastValues = async function(id){
return await getLastData(id)
}
app.getSensors = function( ) {
// return all known sensors
return knownSensors
}
}
function getLastData(id){
return new Promise(function(resolve,reject){
db.get(id,function(err,value){
if(err){reject(err)}else{resolve(value)}
})
})
}