commit a5749f61beb532f02141fc3cdeff8395ca816476
parent 9ce981278f4e692959eec3c6f5a9b4ce02595867
Author: Milan Pässler <me@pbb.lc>
Date: Fri, 14 Jun 2019 15:38:52 +0200
parent 9ce981278f4e692959eec3c6f5a9b4ce02595867
Author: Milan Pässler <me@pbb.lc>
Date: Fri, 14 Jun 2019 15:38:52 +0200
move to apu
9 files changed, 187 insertions(+), 197 deletions(-)
D
|
191
-------------------------------------------------------------------------------
diff --git a/index.html b/index.html @@ -15,6 +15,6 @@ body { <body> <smarthome-layout></smarthome-layout> <noscript>JavaScript is required to use Smart Home</noscript> - <script type="module" src="main.js"></script> + <script type="module" src="main.min.js"></script> </body> </html>
diff --git a/server/index.js b/server/index.js @@ -0,0 +1,181 @@ +"use strict"; + +const url = require("url"); +const http = require("http"); +const net = require("net"); +const modbus = require("modbus-tcp"); +const WebSocket = require("ws"); +const fetch = require("node-fetch"); + +const state = {}; + +function broadcastState() { + wss.broadcast(JSON.stringify(state)); +} + +const server = http.createServer(function(req, res) { + let requestUrl = url.parse(req.url); + if (req.method === "GET" && requestUrl.pathname === "/powermeter.json") { + res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" }); + res.end(JSON.stringify(state.powermeter)); + } else { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end('404'); + } +}); + +const wss = new WebSocket.Server({ server }); + +wss.broadcast = function broadcast(data) { + wss.clients.forEach(function each(client) { + if (client.readyState === WebSocket.OPEN) { + client.send(data); + } + }); +}; + +wss.on('connection', function connection(ws) { + ws.send(JSON.stringify(state)); +}); + +setInterval(function sendKeepalive() { + wss.broadcast(""); +}, 1000); + + +const client = new modbus.Client(); +const socket = new net.Socket(); +socket.on("close", function(e) { + console.log("reconnecting", e); + socket.setTimeout(300, function() { + socket.connect({'host': '192.168.1.1', 'port': 502 }); + }); +}); +socket.connect({'host': '192.168.1.1', 'port': 502 }); +client.pipe(socket); + +/* lights */ + +const lights = [ + "Deckenbeleuchtung", + "Bett", + "Kueche", + "Bad", +]; + +wss.on('connection', function connection(ws) { + ws.on('message', function incoming(message) { + const input = String(message).split(' '); + + if (input[0] == 'set' && input[1] < 9 && (input[2] == 'on' || input[2] == 'off')) { + let val = 0; + + if (input[2] == 'on') { + val = 1; + } + + setRelay(input[1], val); + } + }); +}); + +function mapToNames(data) { + return lights.map((name, id) => { + return { + name, + id: id + 1, + value: data[id], + }; + }); +} + +function setRelay(id, val) { + client.writeSingleCoil(10, '10'+id, val); + client.readCoils(10, 101, 108, function (err, data){ + state.lights = mapToNames(data); + broadcastState(); + }) +} + +function readRelays(ws) { + client.readCoils(10, 101, 108, function (err, data){ + state.lights = mapToNames(data); + }); +} + +readRelays(); + +/* powermeter */ + +state.powermeter = { + 60: { name: "Kueche" }, + 50: { name: "Sonstiges" }, +}; + +const registers = { + voltage: 0, + power: 12, + frequency: 70, + import: 72, + cosphi: 30, +}; + +function unixTimestamp() { + return Math.floor(Number(new Date()) / 1000); +} + +function round(num, places) { + const factor = Math.pow(10, places); + return Math.round(num * factor) / factor; +} + +function getFloat32Register(counterId, registerId, client, socket) { + return new Promise(function(fulfill, reject) { + client.readInputRegisters(counterId, registerId, registerId+1, function (err, data) { + if (err) return reject(); + + // Create a buffer + var buf = new ArrayBuffer(4); + // Create a data view of it + var view = new DataView(buf); + + // set bytes + view.setUint8(0, data[0][0]); + view.setUint8(1, data[0][1]); + view.setUint8(2, data[1][0]); + view.setUint8(3, data[1][1]); + + //const buf = Buffer.concat(data).buffer; + //const view = new DataView(buf); + const num = view.getFloat32(0); + + fulfill(num); + }); + }); +} + +async function fetchValues() { + for (let [id, counter] of Object.entries(state.powermeter)) { + for (let key of Object.keys(registers)) { + try { + counter.values[key] = round(await getFloat32Register(id, registers[key], client, socket), 2); + counter.lastUpdated = unixTimestamp(); + } catch(e) {} + } + } + broadcastState(); +} + + +for (let [id, counter] of Object.entries(state.powermeter)) { + counter.values = Object.keys(registers).reduce(function(result, key) { + result[key] = 0; + return result; + }, {}); + counter.lastUpdated = unixTimestamp(); +} + +setInterval(fetchValues, 20000); +fetchValues(); + +server.listen(8080);
diff --git a/src/layout.js b/src/layout.js @@ -16,14 +16,14 @@ import "./settings.js"; import "./spinner.js"; import "./row.js"; -const luciRedirect = document.createElement("script"); -luciRedirect.innerHTML = "window.location = `http://${window.location.host}/cgi-bin/luci/`;"; +const netdataRedirect = document.createElement("script"); +netdataRedirect.innerHTML = "window.location = `http://${window.location.host}/netdata/`;"; const pages = { "#/lights": { name: "Lights", content: html`<smarthome-lights></smarthome-lights>`, icon: "lightbulb" }, "#/powermeter": { name: "Power Meter", content: html`<smarthome-power-meter></smarthome-power-meter><smarthome-power-meter-history></smarthome-power-meter-history>`, icon: "power" }, "#/departures": { name: "Departures", content: html`<smarthome-departures></smarthome-departures>`, icon: "departure_board" }, - "#/luci": { name: "LuCI", content: html`<smarthome-spinner></smarthome-spinner> ${luciRedirect}`, icon: "router" }, + "#/netdata": { name: "netdata", content: html`<smarthome-spinner></smarthome-spinner> ${netdataRedirect}`, icon: "show_chart" }, "#/settings": { name: "Settings", content: html`<smarthome-settings></smarthome-settings>`, icon: "settings" }, };
diff --git a/src/state.js b/src/state.js @@ -28,7 +28,7 @@ class State { } _initWS() { - this.ws = new WebSocket(`${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.hostname}/${window.location.pathname}/relay/ws`); + this.ws = new WebSocket(`${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.hostname}/${window.location.pathname}/ws`); this.ws.onclose = () => { this.connected = false; this._initWS();
diff --git a/sw.js b/sw.js @@ -9,7 +9,7 @@ let preCache = [ 'https://fonts.gstatic.com/s/materialicons/v47/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2', ]; -const CACHE = 'cache-v5'; +const CACHE = 'cache-v6'; self.addEventListener('install', function (evt) { self.skipWaiting();
diff --git a/websocket-relay/server.js b/websocket-relay/server.js @@ -1,191 +0,0 @@ -"use strict"; - -const http = require("http"); -const net = require("net"); -const modbus = require("modbus-tcp"); -const WebSocket = require("ws"); -const fetch = require("node-fetch"); - -const state = {}; - -function broadcastState() { - wss.broadcast(JSON.stringify(state)); -} - -const server = http.createServer(function(req, res) { - let requestUrl = url.parse(req.url); - if (req.method === "GET" && requestUrl.pathname === "/powermeter.json") { - res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" }); - res.end(JSON.stringify(state.powermeter)); - } else { - res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('404'); - } -}); - -const wss = new WebSocket.Server({ server }); - -wss.broadcast = function broadcast(data) { - wss.clients.forEach(function each(client) { - if (client.readyState === WebSocket.OPEN) { - client.send(data); - } - }); -}; - -wss.on('connection', function connection(ws) { - ws.send(JSON.stringify(state)); -}); - -setInterval(function sendKeepalive() { - wss.broadcast(""); -}, 1000); - -/* lights */ - -const lights = [ - "Deckenbeleuchtung", - "Bett", - "Kueche", - "Bad", -]; - -wss.on('connection', function connection(ws) { - ws.on('message', function incoming(message) { - const input = String(message).split(' '); - - if (input[0] == 'set' && input[1] < 9 && (input[2] == 'on' || input[2] == 'off')) { - let val = 0; - - if (input[2] == 'on') { - val = 1; - } - - setRelay(input[1], val); - } - }); -}); - -function mapToNames(data) { - return lights.map((name, id) => { - return { - name, - id: id + 1, - value: data[id], - }; - }); -} - -function setRelay(id, val) { - const client = new modbus.Client(); - const socket = new net.Socket(); - - - socket.connect({'host': '192.168.1.1', 'port': 502 }); - client.pipe(socket); - - client.writeSingleCoil(10, '10'+id, val); - - client.readCoils(10, 101, 108, function (err, data){ - state.lights = mapToNames(data); - broadcastState(); - }) - - socket.end(); -} - -function readRelays(ws) { - const client = new modbus.Client(); - const socket = new net.Socket(); - - - socket.connect({'host': '192.168.1.1', 'port': 502 }); - client.pipe(socket); - - client.readCoils(10, 101, 108, function (err, data){ - state.lights = mapToNames(data); - }); - - socket.end(); -} - -readRelays(); - -/* powermeter */ - -state.powermeter = { - 60: { name: "Kueche" }, - 50: { name: "Sonstiges" }, -}; - -const registers = { - voltage: 0, - power: 12, - frequency: 70, - import: 72, - cosphi: 30, -}; - -function unixTimestamp() { - return Math.floor(Number(new Date()) / 1000); -} - -function getFloat32Register(counterId, registerId, client, socket) { - return new Promise(function(fulfill, reject) { - client.readInputRegisters(counterId, registerId, registerId+1, function (err, data) { - if (err) return reject(); - - // Create a buffer - var buf = new ArrayBuffer(4); - // Create a data view of it - var view = new DataView(buf); - - // set bytes - view.setUint8(0, data[0][0]); - view.setUint8(1, data[0][1]); - view.setUint8(2, data[1][0]); - view.setUint8(3, data[1][1]); - - //const buf = Buffer.concat(data).buffer; - //const view = new DataView(buf); - const num = view.getFloat32(0); - - fulfill(num); - }); - }); -} - -async function fetchValues() { - const client = new modbus.Client(); - const socket = new net.Socket(); - - - socket.connect({'host': '192.168.1.1', 'port': 502 }); - client.pipe(socket); - - for (let [id. counter] of Object.entries(state.powermeter)) { - for (let key of Object.keys(registers)) { - try { - counter.values[key] = await getFloat32Register(id, registers[key], client, socket); - counter.lastUpdated = unixTimestamp(); - } catch(e) {} - } - } - socket.end(); - - broadcastState(); -} - - -for (let [id, counter] of state.powermeter) { - counter.values = Object.keys(registers).reduce(function(result, key) { - result[key] = 0; - return result; - }, {}); - counter.lastUpdated: unixTimestamp(); -} - -setInterval(fetchValues, 60000); -fetchValues(); - -server.listen(8080);