iobroker.sainlogic
Version:
Read data from a sainlogic based weather station
220 lines (181 loc) • 6.77 kB
JavaScript
/* jslint node: true */
'use strict';
const { COMMANDS } = require('./constants');
const Gateway = require('./gateway');
const net = require('node:net');
const Parser = require('expr-eval').Parser;
const { DATAFIELDS } = require('./constants');
/**
* Scheduler class
*/
class Scheduler {
/**
* @param {object} config configuration object
* @param {object} adapter web adapter object
* @returns object instance
*/
constructor(config, adapter) {
this.ws_address = config.ws_address;
this.port = config.ws_port;
this.interval = config.ws_freq;
this.gw_address = config.gw_address;
this.adapter = adapter;
this.activeCalls = [];
// select activated calls
for (const cmd of COMMANDS) {
if (cmd.configurable) {
const cfg = cmd.config_variable;
if (cfg && config[cfg]) {
adapter.log.info(`Scheduler call ${cmd.config_variable} activated`);
this.activeCalls.push(cmd);
}
}
}
this.fwClient = new net.Socket();
// firmware
this.fwClient.on('data', this.parse_response.bind(this));
this.fwClient.on('close', this.fwClient_close.bind(this));
this.fwClient.on('error', this.server_error.bind(this));
this.fwClientConnected = false;
this.schedule_timer = null;
this.run = 0;
this.adapter.log.debug(`Weatherstation IP: ${this.ws_address}`);
this.adapter.log.debug(`Weatherstation port: ${this.port}`);
this.adapter.log.debug(`Scheduler Interval: ${this.interval}`);
}
/**
* starts the scheduler
*/
start() {
if (this.interval > 0) {
this.schedule_timer = setInterval(() => this.getUpdateFromWS(), this.interval * 1000);
} else {
this.adapter.log.error('Configuration error, interval cannot be 0');
}
}
/**
* stops the scheduler
*/
stop() {
if (this.schedule_timer) {
clearInterval(this.schedule_timer);
}
this.fwClient.destroy();
}
/**
* gets update from weather station
*/
getUpdateFromWS() {
if (this.fwClientConnected) {
this.adapter.log.info(
'Scheduler was not finished getting data from former run, check details or increase interval',
);
// We are still connected, so last request was somehow timed out?
this.fwClient.destroy();
}
this.adapter.log.info('Scheduler pulling for new data');
this.run = 0;
if (this.ws_address != '') {
this.fwClient.connect(this.port, this.ws_address, () => {
this.fwClientConnected = true;
this.client_connect();
});
}
// added calls to WFC01
if (this.gw_address != '') {
const gw = new Gateway(this.gw_address, this.adapter);
gw.run();
}
}
/**
* error handler for server
*
* @param e Error object
*/
server_error(e) {
this.adapter.log.error(`Connection error on ${this.ws_address || '0.0.0.0'}:${this.port}: ${e}`);
}
/**
* connects to the weather station client
*/
client_connect() {
const current_call = this.activeCalls[this.run];
this.adapter.log.debug(`Scheduler connected to weather station run ${current_call.command}`);
this.fwClient.write(new Uint8Array(current_call.command));
}
/**
* parses the response from the weather station
*
* @param data Data buffer received from the weather station
*/
parse_response(data) {
this.adapter.log.debug(`FW Scheduler Received data string: ${data.toString('hex')}`);
const buf = Buffer.from(data.toString('hex'), 'hex');
let objData = COMMANDS[0].parser.parse(buf);
this.adapter.log.debug(`Data Command received: ${objData.command} subcommand ${objData.subcommand}`);
let dataparser, channel;
for (const cmd of this.activeCalls) {
if (
parseInt(cmd.command_int) === parseInt(objData.command) &&
parseInt(cmd.subcommand_int) === parseInt(objData.subcommand)
) {
dataparser = cmd.parser;
channel = cmd.channel;
break;
}
}
if (dataparser) {
objData = dataparser.parse(buf);
this.adapter.log.debug(`Data object: ${JSON.stringify(objData)}`);
this.updateIODataStore(channel, objData);
} else {
this.adapter.log.error(
`Scheduler received data for unkown command ${objData.command} subcommand ${objData.subcommand}`,
);
}
this.run++;
if (this.run < this.activeCalls.length) {
this.client_connect();
} else {
this.fwClient.destroy();
}
}
/**
* updates the IOBroker data store with the received JSON data
*
* @param channel Channel to update
* @param json_data JSON data received from the weather station
*/
updateIODataStore(channel, json_data) {
this.adapter.log.info('Scheduler updating IOBroker states');
const myobj = {};
const parser = new Parser();
// add timestamp conversion function to parser
parser.functions.timestamp_convert = function (timestamp) {
timestamp = `0000${parseInt(timestamp).toString(16)}`.slice(-4);
const hours = `0${parseInt(timestamp.toString(16).substring(0, 2), 16)}`.slice(-2);
const minutes = `0${parseInt(timestamp.toString(16).substring(2), 16)}`.slice(-2);
return `${hours}:${minutes}`;
};
for (const attr in DATAFIELDS) {
// check if this has a mapping to the a data point
const c_id = `${channel}.${DATAFIELDS[attr].id}`;
if (DATAFIELDS[attr].scheduler != null && json_data[DATAFIELDS[attr].scheduler] != null) {
myobj[c_id] = json_data[DATAFIELDS[attr].scheduler];
if (DATAFIELDS[attr].scheduler_conversion != null) {
const exp = parser.parse(DATAFIELDS[attr].scheduler_conversion);
myobj[c_id] = exp.evaluate({ x: myobj[c_id] });
}
}
}
this.adapter.setStates(new Date(), myobj);
}
/**
* closes the weather station client connection
*/
fwClient_close() {
this.adapter.log.debug('FW Scheduler Connection closed');
this.fwClientConnected = false;
}
}
module.exports = Scheduler;