source now in separate modules;

Implemented MQTT coms.
Using Tasmota for ESP8266;
IR working reliably
This commit is contained in:
d-popov
2020-05-02 11:45:35 +03:00
parent 32782cfbf7
commit d8a37e4b44
13 changed files with 3534 additions and 350 deletions

141
src/auth.js Normal file
View File

@@ -0,0 +1,141 @@
// module.exports = function ensureLoggedIn(req, res, next) {
// if (req.isAuthenticated()) { return next(null); }
// res.redirect('/login');
// };
const util = require('util');
var app = require('express')();
var db = require('./db');
var passport = require('passport');
var Strategy = require('passport-local').Strategy;
var CookieStrategy = require('passport-cookie').Strategy;
var session = require('express-session');
const cookierParser = require('cookie-parser');
var ensureLoggedIn = require("connect-ensure-login").ensureLoggedIn("/n/login");
module.exports = {
ensureLoggedIn_Orig: ensureLoggedIn,
ensureLoggedIn_P: function (req, res, next){passport.authenticate('local', {
successRedirect: '/n/accontrol',
failureRedirect: '/n/login' })},
passport: passport,
ensureLoggedIn_New: function (req, res, next) {
if (req.isAuthenticated()) {
console.log("auth OK");
return next(null);
}
console.log("auth redirect");
res.redirect('/login');
},
ensureLoggedIn: function (options) {
if (typeof options == 'string') {
options = { redirectTo: options }
}
options = options || {};
var url = options.redirectTo || '/login';
var setReturnTo = (options.setReturnTo === undefined) ? true : options.setReturnTo;
return function(req, res, next) {
console.log("auth:" + req.isAuthenticated);
if (!req.isAuthenticated || !req.isAuthenticated()) {
if (setReturnTo && req.session) {
req.session.returnTo = req.originalUrl || req.url;
}
return res.redirect(url);
}
next();
}
},
init: function () {
//! Configure the local strategy for use by Passport.
//
// The local strategy require a `verify` function which receives the credentials
// (`username` and `password`) submitted by the user. The function must verify
// that the password is correct and then invoke `cb` with a user object, which
// will be set at `req.user` in route handlers after authentication.
passport.use(new Strategy(
function(username, password, cb) {
console.log('requesting authentication for user '+ username);
db.users.findByUsername(username, function(err, user) {
if (err) {console.log('err:'+ util.inspect(err)); return cb(err); }
if (!user) { console.log('user is null:'); return cb(null, false); }
if (user.password != password) { console.log('wrong pass '); return cb(null, false); }
console.log('authenticated!');
return cb(null, user);
});
}));
// passport.use(new CookieStrategy(
// function(token, done) {
// User.findByToken({ token: token }, function(err, user) {
// if (err) { return done(err); }
// if (!user) { return done(null, false); }
// return done(null, user);
// });
// }
// ));
// Configure Passport authenticated session persistence.
//
// In order to restore authentication state across HTTP requests, Passport needs
// to serialize users into and deserialize users out of the session. The
// typical implementation of this is as simple as supplying the user ID when
// serializing, and querying the user record by ID from the database when
// deserializing.
passport.serializeUser(function(user, cb) {
console.log("serializeUser:"+ util.inspect(user) );
cb(null, user.id);
});
passport.deserializeUser(function(id, cb) {
console.log("deserializeUser:"+ id );
db.users.findById(id, function (err, user) {
if (err) { return cb(err); }
cb(null, user);
});
});
app.use(session({
key: 'user_sid',
secret: е_първият_ще генерира-грешка',
resave: true,
saveUninitialized: false,
cookie: {
expires: 600000
},
}));
app.use(cookierParser('abcdef-12345'))
app.use(passport.initialize());
app.use(passport.session());
app.get('/login', function(req, res) {
res.render('login', { user: req.user });
});
app.post('/login',
passport.authenticate('local', {
successRedirect: '/n/accontrol',
failureRedirect: '/n/login' }),
// authenticated user.
function(req, res) {
console.log("logged in. session:" + req.session);
res.redirect(req.session);
}
);
app.get('/logout', function(req, res){
req.logout();
res.redirect('/n/login');
});
return app;
}
};

69
src/db/database.js Normal file
View File

@@ -0,0 +1,69 @@
const Sequelize = require("sequelize")
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let DeviceMessageSchema = new Schema({
_id: {type: Number, required: true},
device_id: {type: String, required: true, max: 100}
});
let DevicesSchema = new Schema({
id: {type: Number, required: true},
url: {type: String, required: true, max: 100}
});
var sqlz = new Sequelize('iot', 'iot', '!iot_popovi',{dialect: 'mysql'})
var Device = sqlz.define('device', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
name: Sequelize.STRING,
baseurl: Sequelize.TEXT,
apikey: Sequelize.TEXT,
//config: Sequelize.JSON,
lastseen: Sequelize.DATE
});
var DeviceMessage = sqlz.define('devicemessage', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
device_id: { type: Sequelize.INTEGER, allowNull: false},
//id,"device_id","field_name","field_value","timestamp"
field_name: { type: Sequelize.STRING(120), allowNull: false},
field_value: { type: Sequelize.TEXT, allowNull: false},
timestamp: { type: Sequelize.DATE, allowNull: false},
});
var DeviceCommand = sqlz.define("command", {
device: { type: Sequelize.STRING},
command: {type: Sequelize.TEXT},
info: {type: Sequelize.STRING},
ac_power: Sequelize.BOOLEAN,
ac_mode: Sequelize.ENUM('Auto', 'Heat', 'Cool', "Fan"),
ac_fan: Sequelize.ENUM('Auto', 'Low', 'Med', "Hi"),
ac_temp: Sequelize.FLOAT,
ac_turbo: Sequelize.BOOLEAN,
ac_swing: Sequelize.BOOLEAN,
ac_display: Sequelize.BOOLEAN,
ac_econo: Sequelize.BOOLEAN,
ac_health: Sequelize.BOOLEAN,
});
sqlz.sync(
//{ force: true }
)
.then(() => {
console.log(`Database & tables created!`)
})
module.exports = {
init: function() {
//console.log(DeviceCommand.rawAttributes.states.values);
sqlz.sync();
},
sqlz,
Device,
DeviceCommand,
DeviceMessage
//etc
}

56
src/db/devicemessages.js Normal file
View File

@@ -0,0 +1,56 @@
//devicemessages
var mysql = require('mysql');
var con = mysql.createConnection({
host : 'localhost',
user : 'iot',
password : '!iot_popovi',
database : 'iot'
});
const got = require('got');
exports.findByName = function(fieldName, cb) {
process.nextTick(function() {
con.query("SELECT * FROM devicemessages WHERE field_name=? OR ? IS NULL",
[fieldName, fieldName], (err, data) => {
if (!err) {
cb(null, data);
} else {
cb(new Error('SQL Error: ' + err));
}
});
});
}
exports.insert = function(device_id, field_name, value, cb){
var params = [device_id, field_name,value];
let sql = `INSERT INTO devicemessages(device_id,field_name,field_value,timestamp)
VALUES (?,?,?,NOW());`;
con.query(sql,params,(err, r) => {
//if(!cb) {return;}
if (err) {
console.log("error: ", err);
cb && cb(new Error('SQL Error: ' + err));
}else{
console.log("inserted record: ", { id: r.insertId});
cb && cb(null, { id: r.insertId, ...params });
}
});
}
exports.getFromDht = function(url, cb)
{
(async () => {
try {
const dht = await got('http://192.168.1.126/json')
var j = JSON.parse(body);
if(j.dht && j.dht.hum <= 100 && j.dht.hum >= 0){
exports.insert(0, "A23_DHT", dht.body, cb);
}else {
console.log("Skip! Got invalid data from DHT: " + dht);
}
} catch (ex) {
console.log("DHT exception:" + ex);
cb && cb(new Error('SQL Error: ' + ex));
}
})();
}

23
src/db/index.js Normal file
View File

@@ -0,0 +1,23 @@
// //!database
var data = require('./database.js');
data.init();
// var device = data.Device.build({
// name: 'A23',
// url: "http://192.168.1.126/"
// });
// device.save().then().catch(
// err => {console.log(err);}
// )
//.success(function(savedTask) {
// console.log('device saved with id' + savedTask.id);
// });
exports.devicemessages = require('./devicemessages');
exports.users = require('./users');

28
src/db/users.js Normal file
View File

@@ -0,0 +1,28 @@
var records = [
{ id: 1, username: 'me', password: 'A23', displayName: 'admin', emails: [ { value: 'jack@example.com' } ] }
, { id: 2, username: 'db', password: 'doby', displayName: 'DB', emails: [ { value: 'jill@example.com' } ] }
, { id: 3, username: 'popov', password: 'Zelenakrav@', displayName: 'Doby', emails: [ { value: 'db@example.com' } ] }
];
exports.findById = function(id, cb) {
process.nextTick(function() {
var idx = id - 1;
if (records[idx]) {
cb(null, records[idx]);
} else {
cb(new Error('User ' + id + ' does not exist'));
}
});
}
exports.findByUsername = function(username, cb) {
process.nextTick(function() {
for (var i = 0, len = records.length; i < len; i++) {
var record = records[i];
if (record.username === username) {
return cb(null, record);
}
}
return cb(null, null);
});
}

139
src/devices/ir.js Normal file
View File

@@ -0,0 +1,139 @@
const util = require('util');
const { parse } = require('querystring');
var moment = require('moment');
const got = require('got');
const request = require('request');
function GetDht() {
var result;
(async () => {
try {
var ret = await got('http://192.168.1.126/cm?cmnd=status%2010');
console.log("DHT: "+ util.inspect(ret.body));
var j = JSON.parse(ret.body);
console.log("JSON> " + util.inspect(j));
if(j.StatusSNS && j.StatusSNS.DHT11 && j.StatusSNS.DHT11.Humidity !== null)
{
result = {
dht:{
hum: j.StatusSNS.DHT11.Humidity,
temp: j.StatusSNS.DHT11.Temperature,
dew: j.StatusSNS.DHT11.DewPoint
}};
}
} catch (error) {
console.log(error);
}
})();
while(result === undefined) {
require('deasync').runLoopOnce();
}
return result;
}
exports.SendCmd = function (url, cmd)
{
url = url +"/cm?cmnd=irsend%200,"+cmd;
console.log("IR_SEND_RAW:" + url);
(async () => {
try {
ret = await got(url);
} catch (error) {
console.log(error);
}
})();
// request.get(url,
// function (error, response, body) {
// if (!error && response.statusCode == 200) {
// console.log("GOT '" + util.inspect(body) + "'");
// return true;
// }else{
// console.log("ERROR on SendIRCommand:" + util.inspect(error));
// return false;
// }
// }
// );
// request.post(url, { form: { cmd: cmd } },
// function (error, response, body) {
// if (!error && response.statusCode == 200) {
// console.log("GOT '" + util.inspect(body) + "'");
// return true;
// }else{
// console.log("ERROR on SendIRCommand:" + util.inspect(error));
// return false;
// }
// }
// );
}
module.exports.html_handle_dht = function (req, res){
try {
console.log("body:"+util.inspect(req.body));
var cmd = req.param('e');
console.log("cmd:" + cmd);
//console.log("HEADERS:" + util.inspect(req.headers));
switch(cmd)
{
//if(req.method =="GET")
case 'setup':
console.log("/setup> Device is online: " + req.headers.mac);
break;
case 'ping':
var t = moment.duration(parseInt(req.param('uptime')), 'milliseconds');
var _message = req.param('ip') + " uptime " + t.hours() + "h " + t.minutes() + "m " + t.seconds() +"s";
// var t = moment.duration(parseInt(req.params.uptime), 'milliseconds');
// var _message = req.params.ip + " uptime " + t.hours() + "h " + t.minutes() + "m " + t.seconds() +"s";
console.log("ping from " + _message);
//rs.send("pong=ok");
res.send(t.hours() + "h " + t.minutes() + "m " + t.seconds() +"s");
break;
case "now":
console.log("getting current conditions");
try {
const response = GetDht();
console.log(response);
res.send(response);
} catch (error) {
console.log("DHT Error:" + error);
}
console.log("got current conditions??");
break;
case 'ir':
console.log("got IR message!");
console.log(req.body);
console.log(util.inspect(req.params));
try{
ob = JSON.parse(body);
if(ob.times)
{
console.log("GOT TIMING INFO:");
if(!ob.ir){
if(SendIRCommand(ob.times)) { res.sendStatus(200);}
else { res.sendStatus(500); }
} else {
console.log("It is from the IR reader. Ignoring...");
}
}
BroadcastWS(ob.info.replaceAll('\n','<br/>') + "<br/><br/>" + ob.descr.replaceAll(',', '<br/>') );
}catch(ex){
}
if(req.param('info') && req.param('descr') )
{
console.log("Got Url encoded IR message");
BroadcastWS(req.param('info').replaceAll('\n','<br/>') + "<br/><br/>" + req.param('descr').replaceAll(',', '<br/>'));
}
break;
}
res.sendStatus(200);
} catch (error) {
console.log("ESP Error:" + error);
//res.end();
//res.send(error);
}
}

4
src/utils.js Normal file
View File

@@ -0,0 +1,4 @@
String.prototype.replaceAll = function(search, replacement) {
var target = this;
return target.replace(new RegExp(search, 'g'), replacement);
};