2024-05-07 16:28:43 +02:00
exports . id = 'dido_controller' ;
exports . title = 'DIDO_Controller' ;
exports . version = '2.0.0' ;
exports . group = 'Worksys' ;
exports . color = '#2134B0' ;
2024-09-22 22:18:46 +02:00
exports . input = 2 ;
2024-05-07 16:28:43 +02:00
exports . output = [ "red" , "white" , "yellow" ] ;
exports . click = false ;
exports . icon = 'bolt' ;
exports . options = { edge : "undefined" } ;
exports . html = ` <div class="padding">
2024-09-09 12:32:34 +02:00
< div class = "row" >
< div class = "col-md-6" >
< div data - jc = "textbox" data - jc - path = "edge" data - jc - config = "placeholder:undefined;required:true" class = "m" > Edge TB Name < / d i v >
< / d i v >
< / d i v >
2024-05-07 16:28:43 +02:00
< / d i v > ` ;
exports . readme = ` # Sets RS232 port and all digital pins on device. Then it starts to receive data from sensors.
It receives :
rotary _switch _state ,
rotary _switch _state ,
door _condition ,
state _of _breaker ,
state _of _contactor ,
twilight _sensor
` ;
/ *
we open rsPort "/dev/ttymxc0" and set digital input and output pins with "setRSPortData"
Currently we are interested in pins no . 1 , 2 , 3 , 6 , 8 , 9 , 10 , 16
pins number 11 , 12 , 13 ( we receive 10 , 11 , 12 in rsPortReceivedData ) are "stykace"
When port receives data , it must be exactly 4 bytes long . Second byte is pin , that changed its value , fourth byte is value itself .
After that , we set this value to "previousValues[allPins[whichpin]]" variable
* /
/ *
RVO objekt :
state _of _main _switch - sem sa bude reportovať stav hlavného ističa : 0 - > off 1 - > on ( toto nie je na platforme , ale Rado to už do entity type doplnil )
rotary _switch _state - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky :
Manual = 1 a Auto = 0 - > vyreportuje Manual
Manual = 0 a Auto = 0 - > vyreportuje Off
Manual = 0 a Auto = 1 - > vyreportuje Automatic
door _condition - tuto ide pin 6 , dverový kontakt - > 1 - > vyreportuje Closed , 0 - > vyreportuje Open
twilight _sensor - hodnotu , ktorú vracia ten analógový vstup ( 17 ) treba poslať sem ako float number . Zrejme tu potom pridáme nejaký koeficient prevodu na luxy
zjavne nám v jsone chýba stav hlavného ističa . Musíme to potom doplniť
Na každú líniu :
state _of _breaker - podľa indexu ističa sa reportuje jeho stav , teda istič 1 na líniu 1 : 0 - > off 1 - > on
state _of _contactor - podľa indexu stykača sa reportuje jeho stav , teda stykač 1 na líniu 1 : 0 - > off 1 - > on
momentálne sa stav zmení len keď vo flow klikneš aby sa zmenil , ale tá zmena by sa mala ukázať aj na platforme
* /
//globals
//FIRMWARE version
2024-09-22 22:18:46 +02:00
//TODO remove FLOW.OMS_edgeName variable, as we have FLOW.OMS_rvo_tbname
FLOW . OMS _edge _fw _version = "2024-09-23" ; //rok-mesiac-den
2024-05-07 16:28:43 +02:00
FLOW . OMS _edgeName = "" ;
FLOW . OMS _maintenance _mode = false ;
//dynamic values
FLOW . OMS _masterNodeIsResponding = true ; //cmd_manager
//FLOW.OMS_brokerready = false //wsmqttpublish
FLOW . OMS _no _voltage = new Set ( ) ; //modbus_citysys - elektromer
//see loadSettings() in cmd_manager
FLOW . OMS _language = "en" ; //cmd_manager
FLOW . OMS _rvo _name = "" ; //cmd_manager
FLOW . OMS _rvo _tbname = "" ; //relaysData
FLOW . OMS _temperature _adress = "" ; //cmd_manager
//-----------------------------------------------
let alarmStatus = "OFF" ;
2024-09-09 12:32:34 +02:00
const SEND _TO = {
2024-05-07 16:28:43 +02:00
debug : 0 ,
tb : 1 ,
cmd _manager : 2
}
2024-09-22 22:18:46 +02:00
const TIME _AFTER _TEMPERATURE _NOK _STATUS = 3600 ; //seconds
const DIFFERENCE _TO _SEND _TEMPERATURE = 0.31 ;
2024-05-07 16:28:43 +02:00
var log4js = require ( "log4js" ) ;
var path = require ( 'path' ) ;
log4js . configure ( {
appenders : {
errLogs : { type : 'file' , compress : true , daysToKeep : 2 , maxLogSize : 1048576 , backups : 1 , keepFileExt : true , filename : path . join ( _ _dirname + "/../" , 'err.txt' ) } ,
monitorLogs : { type : 'file' , compress : true , daysToKeep : 2 , maxLogSize : 1048576 , backups : 1 , keepFileExt : true , filename : path . join ( _ _dirname + "/../" , 'monitor.txt' ) } ,
console : { type : 'console' }
} ,
categories : {
errLogs : { appenders : [ 'console' , 'errLogs' ] , level : 'error' } ,
monitorLogs : { appenders : [ 'console' , 'monitorLogs' ] , level : 'trace' } ,
//another: { appenders: ['console'], level: 'trace' },
default : { appenders : [ 'console' ] , level : 'trace' }
}
} ) ;
const errLogger = log4js . getLogger ( "errLogs" ) ;
const logger = log4js . getLogger ( ) ;
const monitor = log4js . getLogger ( "monitorLogs" ) ;
//console.log(path.join(__dirname, 'err.txt', "-----------------------------"));
/ *
process . on ( 'uncaughtException' , function ( err ) {
errLogger . error ( 'uncaughtException:' , err . message )
errLogger . error ( err . stack ) ;
//process.exit(1);
} )
* /
//USAGE
//logger.debug("text")
//monitor.info('info');
//errLogger.error("some error");
exports . install = function ( instance ) {
process . on ( 'uncaughtException' , function ( err ) {
//TODO send to service
errLogger . error ( 'uncaughtException:' , err . message )
errLogger . error ( err . stack ) ;
errorHandler . sendMessageToService ( err . message + "\n" + err . stack , 0 , "js_error" ) ;
//process.exit(1);
} )
2024-09-17 08:54:32 +02:00
// temperature value is initialized to -1000. It can be literally anything, we just needs to be able to enter if block in ws.onmessage function, when first temperatere data comes
let previousValues = { temperature : { value : - 1000 , lastTimeTemperatureReceived : Date . now ( ) / 1000 } } ;
2024-05-07 16:28:43 +02:00
let rsPortReceivedData = [ ] ;
2024-09-17 08:54:32 +02:00
//to be able to get proper twilight values, when
2024-05-07 16:28:43 +02:00
let twilight _sensor _interval = 5 ; //minutes
let twilight _sensor = [ ] ;
const twilight _sensor _array = [ ] ;
let twilightError = false ;
let edgeName = "" ;
2024-09-17 08:54:32 +02:00
monitor . info ( "DIDO_Relay_Controller installed" ) ;
2024-05-07 16:28:43 +02:00
//key is PIN number , line: 0 = RVO
/ *
let conversionTable = {
"1" : { tbname : "" , type : "state_of_main_switch" , "line" : 0 } , //state_of_main_switch pin1
2024-09-09 12:32:34 +02:00
"2" : { tbname : "" , type : "rotary_switch_state" , "line" : 0 } , //rotary_switch_state - poloha manual = pin2
2024-05-07 16:28:43 +02:00
"3" : { tbname : "" , type : "rotary_switch_state" , "line" : 0 } , //rotary_switch_state - poloha auto = pin3
"4" : { tbname : "" , type : "power_supply" , "line" : 0 } ,
"5" : { tbname : "" , type : "battery" , "line" : 0 } ,
"6" : { tbname : "" , type : "door_condition" , "line" : 0 } , // door_condition = pin6, 1 -> vyreportuje Closed, 0 -> vyreportuje Open
"8" : { tbname : "" , type : "state_of_breaker" , "line" : 1 } , // state_of_breaker linia 1 0=off, 1=on
"9" : { tbname : "" , type : "state_of_breaker" , "line" : 2 } , // state_of_breaker linia 2 0=off, 1=on
"10" : { tbname : "" , type : "state_of_breaker" , "line" : 3 } , // state_of_breaker linia 3 0=off, 1=on
"11" : { tbname : "" , type : "state_of_contactor" , "line" : 1 } , // state_of_contactor linia 1 0=off, 1=on
"12" : { tbname : "" , type : "state_of_contactor" , "line" : 2 } , // state_of_contactor linia 2 0=off, 1=on
"13" : { tbname : "" , type : "state_of_contactor" , "line" : 3 } , // state_of_contactor linia 3 0=off, 1=on
"16" : { tbname : "" , type : "twilight_sensor" , "line" : 0 } , // twilight_sensor = pin16
} ;
* /
const dbPins = TABLE ( "pins" ) ;
let pinsData = { } ; //key is pin
const dbRelays = TABLE ( "relays" ) ;
let relaysData = { } ; //key is line
2024-09-17 08:54:32 +02:00
const dbStatus = TABLE ( "status" ) ;
let statusData = null ;
2024-05-07 16:28:43 +02:00
//status for calculating Statecodes
2024-09-22 22:18:46 +02:00
let deviceStatus = { //key is device name: temperature,....
"state_of_main_switch" : "Off" , //Hlavný istič
"rotary_switch_state" : "Off" , //Prevádzkový mód
"door_condition" : "closed" , //Dverový kontakt
"em" : "OK" , //elektromer rvo
"temperature" : "OK" , //templomer
"battery" : "OK" , //Batéria
"power_supply" : "OK" , //Zdroj
"master_node" : "OK" , //MN - FLOW.OMS_masterNodeIsResponding
"no_voltage" : "OK" , //FLOW.OMS_no_voltage - výpadok napätia na fáze
"state_of_breaker" : { } , //"Off",//Istič
"state_of_contactor" : { } , //"Off",//Stykač
"twilight_sensor" : "OK" //lux sensor
} ;
2024-05-07 16:28:43 +02:00
const SerialPort = require ( 'serialport' ) ;
const WebSocket = require ( 'ws' ) ;
let ws = null ;
let rsPort = null ;
2024-09-17 08:54:32 +02:00
const { runSyncExec } = require ( './helper/serialport_helper.js' ) ;
2024-05-07 16:28:43 +02:00
const { bytesToInt , resizeArray } = require ( './helper/utils' ) ;
const { promisifyBuilder , makeMapFromDbResult } = require ( './helper/db_helper.js' ) ;
2024-09-17 08:54:32 +02:00
const { sendNotification } = require ( './helper/notification_reporter.js' ) ;
2024-05-07 16:28:43 +02:00
const bitwise = require ( 'bitwise' ) ;
2024-09-17 08:54:32 +02:00
const DataToTbHandler = require ( './helper/DataToTbHandler.js' ) ;
const tbHandler = new DataToTbHandler ( SEND _TO . tb ) ;
tbHandler . setSender ( exports . title ) ;
2024-05-07 16:28:43 +02:00
const ErrorToServiceHandler = require ( './helper/ErrorToServiceHandler.js' ) ;
const errorHandler = new ErrorToServiceHandler ( ) ;
//let useTurnOffCounter = false;
//let turnOffCounter = 0;
let controller _type = FLOW . OMS _controller _type //"lm" or "unipi" //logicMachine
if ( controller _type == "" ) controller _type = "lm" ;
console . log ( exports . title , "controller type: " , controller _type ) ;
2024-09-17 08:54:32 +02:00
2024-05-07 16:28:43 +02:00
async function loadAllDb ( )
{
let responsePins = await promisifyBuilder ( dbPins . find ( ) ) ;
pinsData = makeMapFromDbResult ( responsePins , "pin" ) ;
let responseRelays = await promisifyBuilder ( dbRelays . find ( ) ) ;
relaysData = makeMapFromDbResult ( responseRelays , "line" ) ;
2024-09-17 08:54:32 +02:00
let responseStatus = await promisifyBuilder ( dbStatus . find ( ) ) ;
2024-09-22 22:18:46 +02:00
statusData = responseStatus [ 0 ] ; // {thermometer: 'OK', em: 'OK', twilight_sensor: 'OK'}
2024-09-17 08:54:32 +02:00
deviceStatus [ "temperature" ] = statusData . thermometer ;
2024-09-22 22:18:46 +02:00
2024-05-07 16:28:43 +02:00
FLOW . OMS _rvo _tbname = relaysData [ 0 ] . tbname ;
if ( controller _type === "lm" )
{
handleRsPort ( ) ;
}
else if ( controller _type === "unipi" )
{
handleWebSocket ( ) ;
}
else {
errLogger . debug ( "UNKNOWN controller_type:" , controller _type ) ;
}
}
function initialSetting ( )
{
//force turn off relays & set tbname
let keys = Object . keys ( pinsData ) ;
for ( let i = 0 ; i < keys . length ; i ++ )
{
let key = keys [ i ] ;
let line = pinsData [ key ] . line ;
if ( line != undefined )
{
if ( relaysData [ line ] != undefined )
{
pinsData [ key ] . tbname = relaysData [ line ] . tbname ;
relaysData [ line ] . contactor = 0 ;
}
else
{
errLogger . error ( "CRITICAL!!! undefined relay" , relaysData [ line ] , line ) ;
2024-09-09 12:32:34 +02:00
//sendNotification("set port ", edgeName, ERRWEIGHT.CRITICAL, "local database is corrupted", "", SEND_TO.tb, instance, null );
sendNotification ( "set port " , edgeName , "local_database_is_corrupted" , { } , "" , SEND _TO . tb , instance ) ;
2024-05-07 16:28:43 +02:00
}
}
if ( pinsData [ key ] . type == "state_of_contactor" )
{
let pin = key - 1 ;
if ( controller _type === "unipi" ) pin = key ;
//this will modify database
let forceTurnOff = true ;
turnOffLine ( line , pin , forceTurnOff , "turn off on startup" ) ;
}
}
//report RVO version relaysData[0].tbname;
let values = { } ;
values [ "edge_fw_version" ] = FLOW . OMS _edge _fw _version ;
values [ "maintenance_mode" ] = FLOW . OMS _maintenance _mode ;
edgeName = relaysData [ 0 ] . tbname ;
FLOW . OMS _edgeName = edgeName ;
dataToTb = {
[ edgeName ] : [
{
"ts" : Date . now ( ) ,
"values" : values
}
]
}
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . tb , dataToTb ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
let time = 5 * 1000 ;
2024-05-07 16:28:43 +02:00
setTimeout ( function ( ) {
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "buildTasks" } ) ;
2024-05-07 16:28:43 +02:00
2024-09-09 12:32:34 +02:00
sendNotification ( "rsPort.open()" , edgeName , "flow_start" , { } , "" , SEND _TO . tb , instance ) ;
2024-05-07 16:28:43 +02:00
monitor . info ( "-->FLOW bol spustený" , edgeName , FLOW . OMS _edge _fw _version ) ;
} , time ) ;
}
function handleRsPort ( )
{
//TODO build according to pins!!!
//! rsPort to open are the same for lm and unipi and electromer ("/dev/ttymxc0")
const setRSPortData = [ 0xAA , 6 , 6 , 6 , 6 , 6 , 6 , 0 , 6 , 6 , 6 , 1 , 1 , 1 , 1 , 0 , 0 , 10 , 10 , 10 , 10 , 10 , 10 , 0 , 10 , 10 , 10 , 0 , 0 , 0 , 0 , 0 , 0 , 5 , 0 , 0 , 0 , 15 , 15 , 15 , 15 , 15 , 15 , 0 , 15 , 15 , 15 , 0 , 0 , 0 , 0 , 0 , 0 , 30 , 0 , 0 , 0 ] ;
rsPort = new SerialPort ( "/dev/ttymxc0" , { autoOpen : false } ) ;
rsPort . on ( 'error' , function ( err ) {
logger . debug ( "rsPort opened error - failed" , err . message ) ;
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . debug , err . message ) ;
2024-05-07 16:28:43 +02:00
errorHandler . sendMessageToService ( exports . title + " rsPort opened error - failed: " + err . message ) ;
} )
rsPort . on ( 'open' , async function ( ) {
2024-09-09 12:32:34 +02:00
await runSyncExec ( "stty -F /dev/ttymxc0 115200 min 1 time 5 ignbrk -brkint -icrnl -imaxbel -opost -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke" ) . then ( function ( status ) {
2024-05-07 16:28:43 +02:00
//set port
rsPort . write ( Buffer . from ( setRSPortData ) , function ( err ) {
monitor . info ( exports . title + "--->Digital in_out has been set (runSyncExec was sucessfull)" ) ;
turnOffAlarm ( ) ;
//useTurnOffCounter = true;
//turnOffCounter = relaysData.length - 1;
initialSetting ( ) ;
} )
} ) . catch ( function ( reason ) {
2024-09-09 12:32:34 +02:00
//instance.send(SEND_TO.debug, exports.title + " runSyncExec - promise rejected:" + reason);
2024-05-07 16:28:43 +02:00
errLogger . error ( exports . title + " runSyncExec - promise rejected:" + reason ) ;
errorHandler . sendMessageToService ( exports . title + " runSyncExec - promise rejected:" + reason ) ;
} ) ;
} ) ;
rsPort . on ( 'data' , function ( data ) {
rsPortReceivedData = [ ... rsPortReceivedData , ... data ] ;
if ( rsPortReceivedData [ 0 ] != 85 ) {
rsPortReceivedData = [ ] ;
return ;
}
let l = rsPortReceivedData . length ;
if ( l < 4 ) return ;
if ( l > 4 ) {
// if array length is greater than 4, we take first 4 byte and do the logic, second 4 bytes, do the logic and so on
let i , j , temparray , chunk = 4 ;
for ( i = 0 , j = l ; i < j ; i += chunk ) {
temparray = rsPortReceivedData . slice ( i , i + chunk ) ;
if ( temparray . length < 4 ) {
rsPortReceivedData = [ ... temparray ] ;
return ;
}
switchLogic ( temparray ) ;
}
rsPortReceivedData = [ ] ;
return ;
}
switchLogic ( rsPortReceivedData ) ;
rsPortReceivedData = [ ] ;
} ) ;
rsPort . on ( "close" , ( ) => {
rsPort . close ( ) ;
} )
rsPort . open ( ) ;
}
function handleWebSocket ( ) {
//to keep websocket opened, we send request every 150 seconds
let startRequests = null ;
console . log ( "handleWebSocket function called" ) ;
ws = new WebSocket ( 'ws:/0.0.0.0:1234/ws' ) ;
ws . onopen = function open ( ) {
instance . send ( 0 , exports . title + " running" ) ;
turnOffAlarm ( ) ;
// useTurnOffCounter = true;
// turnOffCounter = relaysData.length - 1;
initialSetting ( ) ;
ws . send ( JSON . stringify ( { "cmd" : "all" } ) ) ;
// we request dev info about neuron device from evok to keep websocket connection alive
// for some reason this request returns no data, but connection keeps alive
// https://evok.api-docs.io/1.0/mpqzDwPwirsoq7i5A/websocket
startRequests = setInterval ( ( ) => {
// console.log(" *** data from evok requested");
ws . send ( JSON . stringify ( { "cmd" : "filter" , "dev" : [ "neuron" ] } ) ) ;
} , 150000 )
} ;
2024-09-17 08:54:32 +02:00
2024-05-07 16:28:43 +02:00
// SAMPLE DATA FROM WEBSOCKET
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_07',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_08',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
2024-09-22 22:18:46 +02:00
ws . onmessage = async function ( data ) {
2024-05-07 16:28:43 +02:00
data = JSON . parse ( data . data ) ;
// data comes in array except of "temperature" ==> it comes as an object
2024-09-17 08:54:32 +02:00
if ( isObject ( data ) )
2024-05-07 16:28:43 +02:00
{
let value = data [ 'value' ] ;
2024-09-22 22:18:46 +02:00
const values = { } ;
2024-09-17 08:54:32 +02:00
previousValues [ "temperature" ] [ "lastTimeTemperatureReceived" ] = data [ 'time' ] ;
2024-05-07 16:28:43 +02:00
2024-09-22 22:18:46 +02:00
// we received data from thermometer, but thermometer status is NOK:
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "temperature" ] === "NOK" )
2024-05-07 16:28:43 +02:00
{
2024-09-22 22:18:46 +02:00
await writeThermometerStatusToDb ( "OK" ) ;
sendRvoStatus ( ) ;
2024-09-17 08:54:32 +02:00
}
// temperature value comes very often. To handle it, we check if it change for more than 0.3 degrees, if yes, we send to TB
2024-09-22 22:18:46 +02:00
if ( Math . abs ( previousValues [ "temperature" ] [ "value" ] - value ) > DIFFERENCE _TO _SEND _TEMPERATURE )
2024-09-17 08:54:32 +02:00
{
previousValues [ "temperature" ] [ "value" ] = value ;
2024-09-22 22:18:46 +02:00
values [ 'temperature' ] = value ;
sendTelemetry ( values , FLOW . OMS _rvo _tbname ) ;
2024-05-07 16:28:43 +02:00
}
return ;
}
data . map ( item => {
let value = item [ 'value' ] ;
let pin = item [ "dev" ] + item [ "circuit" ] ; // for example "relay1_03" or "input1_01"
if ( pin == undefined ) return ;
switchLogic ( pin , value ) ;
} )
}
ws . on ( 'error' , ( err ) => {
2024-05-14 16:29:11 +02:00
monitor . info ( 'websocket error, reconnect' )
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . debug , err . message ) ;
2024-05-07 16:28:43 +02:00
clearInterval ( startRequests ) ;
2024-05-14 16:29:11 +02:00
ws = null ;
setTimeout ( handleWebSocket , 1000 ) ;
2024-05-07 16:28:43 +02:00
} )
ws . onclose = function ( ) {
// connection closed, discard old websocket and create a new one in 5s
// stopRequests();
2024-05-14 16:29:11 +02:00
monitor . info ( 'websocket onclose, reconnect' )
2024-05-07 16:28:43 +02:00
clearInterval ( startRequests ) ;
ws = null ;
console . log ( "ws is null now, reconnecting..." ) ;
setTimeout ( handleWebSocket , 1000 ) ;
}
}
// ! do we need requests every minute ???
// const startRequests = () => {
// console.log("startRequest function called");
// start = setInterval(() => {
// // console.log("data from evok requested");
// ws.send(JSON.stringify({"cmd":"filter", "devices": "neuron"}));
// // ws.send(JSON.stringify({"cmd":"filter", "devices":["input", "relay"]}));
// }, 150000)
// }
instance . on ( "close" , ( ) => {
if ( rsPort ) rsPort . close ( ) ;
if ( ws ) ws . close ( ) ;
clearInterval ( sendRebuildTasksAt11 ) ;
} )
loadAllDb ( ) ;
2024-09-17 08:54:32 +02:00
2024-05-07 16:28:43 +02:00
function getPin ( line )
{
//conversionTable
let keys = Object . keys ( pinsData ) ;
for ( let i = 0 ; i < keys . length ; i ++ )
{
let key = keys [ i ] ;
if ( pinsData [ key ] . type == "state_of_contactor" && pinsData [ key ] . line == line )
{
if ( rsPort ) return key - 1 ;
if ( ws ) return key ;
}
}
logger . debug ( "no pin detected" ) ;
return null ;
}
function turnOnAlarm ( )
{
if ( FLOW . OMS _maintenance _mode ) return ;
alarmStatus = "ON" ;
if ( rsPort )
{
let arr = [ 0x55 ] ;
arr . push ( 13 ) ;
arr . push ( 0 ) ;
arr . push ( 1 ) ;
rsPort . write ( Buffer . from ( arr ) , function ( err ) {
logger . debug ( "sirena zapnuta" ) ;
} ) ;
}
else if ( ws )
{
let cmd = { "cmd" : "set" , "dev" : "relay" , "circuit" : "1_01" , "value" : 1 } ;
ws . send ( JSON . stringify ( cmd ) ) ;
logger . debug ( "sirena zapnuta" ) ;
}
}
function turnOffAlarm ( )
{
alarmStatus = "OFF" ;
if ( rsPort )
{
let arr = [ 0x55 ] ;
arr . push ( 13 ) ;
arr . push ( 0 ) ;
arr . push ( 0 ) ;
rsPort . write ( Buffer . from ( arr ) , function ( err ) {
logger . debug ( "sirena vypnuta" ) ;
} ) ;
}
else if ( ws )
{
let cmd = { "cmd" : "set" , "dev" : "relay" , "circuit" : "1_01" , "value" : 0 } ;
ws . send ( JSON . stringify ( cmd ) ) ;
logger . debug ( "sirena vypnuta" ) ;
}
}
function reportLineStatus ( line )
{
//Tá hodnota by mala fungovať tak že LSB bit číslo je stav ističa (1 - On, 0 - Off) a druhý bit je stav stýkača (1 - true, 0 - false).
let tbname = relaysData [ line ] . tbname ;
let bits = [ ] ;
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "state_of_breaker" ] [ line ] == "On" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 0 ) ;
}
else bits . push ( 1 ) ;
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "state_of_contactor" ] [ line ] == "On" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 0 ) ;
}
else bits . push ( 1 ) ;
resizeArray ( bits , 8 , 0 ) ;
let byte = bitwise . byte . write ( bits . reverse ( ) ) ;
//console.log("line", line, bits, byte);
let values = {
statecode : byte
}
let dataToTb = {
[ tbname ] : [
{
"ts" : Date . now ( ) ,
"values" : values
}
]
}
//console.log(values);
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . tb , dataToTb ) ;
2024-05-07 16:28:43 +02:00
}
function turnOnLine ( line , pin , force , info )
{
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . debug , "turn on line " + line ) ;
2024-05-07 16:28:43 +02:00
if ( force == undefined ) force = false ;
if ( line == 0 )
{
if ( alarmStatus == "ON" ) turnOffAlarm ( ) ;
FLOW . OMS _maintenance _mode = true ;
let values = { } ;
values [ "statecode" ] = calculateStateCode ( ) ;
values [ "power_mode" ] = "maintenance" ;
let tbname = relaysData [ line ] . tbname ;
sendTelemetry ( values , tbname ) ;
monitor . info ( "turnOnLine (line, FLOW.OMS_maintenance_mode)" , line , FLOW . OMS _maintenance _mode , info ) ;
return ;
}
if ( pin === undefined ) pin = getPin ( line ) ;
monitor . info ( "turnOnLine (line, pin, force)" , line , pin , force , info ) ;
if ( pin === undefined )
{
monitor . info ( "pin is undefined!" , line ) ;
return ;
}
if ( ! force )
{
if ( relaysData [ line ] . contactor == 1 )
{
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . debug , "line is already on " + line ) ;
2024-05-07 16:28:43 +02:00
logger . debug ( "turnOnLine: line is already on: " , line ) ;
return ;
}
}
// if(!rsPort.isOpen && !ws)
if ( ! rsPort && ! ws )
{
2024-05-14 16:29:11 +02:00
errLogger . error ( "dido controller - port or websocket is not opened" ) ;
2024-05-07 16:28:43 +02:00
return ;
}
if ( rsPort )
{
let arr = [ 0x55 ] ;
arr . push ( pin ) ;
arr . push ( 0 ) ;
arr . push ( 1 ) ;
rsPort . write ( Buffer . from ( arr ) , function ( err ) {
if ( err === undefined )
{
console . log ( "turnONLine zapisal do rsPortu" , line , arr ) ;
switchLogic ( arr ) ;
}
else
{
monitor . info ( "turnOnLine WRITE error" , err ) ;
}
} ) ;
}
else if ( ws )
{
console . log ( "turnONLine pin (relay)" , pin ) ;
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
let cmd = { "cmd" : "set" , "dev" : "relay" , "circuit" : pin . slice ( 5 ) , "value" : 1 } ;
ws . send ( JSON . stringify ( cmd ) ) ;
2024-09-17 08:54:32 +02:00
switchLogic ( pin , 1 )
2024-05-07 16:28:43 +02:00
}
}
function turnOffLine ( line , pin , force , info )
{
if ( force == undefined ) force = false ;
if ( line == 0 )
{
FLOW . OMS _maintenance _mode = false ;
let values = { } ;
values [ "statecode" ] = calculateStateCode ( ) ;
values [ "power_mode" ] = "automatic" ;
let tbname = relaysData [ line ] . tbname ;
sendTelemetry ( values , tbname ) ;
return ;
}
if ( pin === undefined ) pin = getPin ( line ) ;
monitor . info ( "turnOffLine (line, pin, force)" , line , pin , force , info ) ;
if ( pin === undefined )
{
errLogger . error ( "pin is undefined!" , line ) ;
return ;
}
if ( ! force )
{
if ( relaysData [ line ] . contactor == 0 )
{
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . debug , "line is already off " + line ) ;
2024-05-07 16:28:43 +02:00
logger . debug ( "turnOffLine: line already off:" , line ) ;
return ;
}
}
// if(!rsPort.isOpen && !ws)
if ( ! rsPort && ! ws )
{
2024-09-17 08:54:32 +02:00
errLogger . error ( "dido controller - port or websocket is not opened" ) ;
2024-05-07 16:28:43 +02:00
return ;
}
if ( rsPort )
{
let arr = [ 0x55 ] ;
arr . push ( pin ) ;
arr . push ( 0 ) ;
arr . push ( 0 ) ;
rsPort . write ( Buffer . from ( arr ) , function ( err ) {
if ( err === undefined )
{
console . log ( "turnOffLine zapisal do rsPort-u" , line , arr ) ;
switchLogic ( arr ) ;
}
else
{
monitor . info ( "turnOffLine WRITE error" , err ) ;
}
} ) ;
}
else if ( ws )
{
//pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
//monitor.info("turnOffLine pin (relay)", pin);
let cmd = { "cmd" : "set" , "dev" : "relay" , "circuit" : pin . slice ( 5 ) , "value" : 0 } ;
ws . send ( JSON . stringify ( cmd ) ) ;
2024-09-17 08:54:32 +02:00
switchLogic ( pin , 0 )
2024-05-07 16:28:43 +02:00
}
}
//data from modbus_reader or temperature sensor or twilight sensor or other modbus device
instance . on ( "0" , flowdata => {
if ( ! flowdata . data instanceof Object ) return ;
// console.log('***********************', flowdata.data)
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . debug , flowdata . data ) ;
2024-05-07 16:28:43 +02:00
// we handle nok status from modbus_reader component and thermometer
if ( flowdata . data ? . status )
{
const status = flowdata . data . status ;
if ( status == "NOK-twilight_sensor" )
{
2024-09-17 08:54:32 +02:00
deviceStatus [ "twilight_sensor" ] = "NOK" ;
2024-05-07 16:28:43 +02:00
}
else if ( status == "NOK-em340" || status == "NOK-em111" )
{
2024-09-17 08:54:32 +02:00
deviceStatus [ "em" ] = "NOK" ;
2024-05-07 16:28:43 +02:00
}
2024-09-17 08:54:32 +02:00
//"NOK-thermometer" comes just from LM. Unipi handles thermometer from ws evok.
2024-05-07 16:28:43 +02:00
else if ( status == "NOK-thermometer" )
{
2024-09-17 08:54:32 +02:00
previousValues [ "temperature" ] [ "lastTimeTemperatureReceived" ] = null ;
deviceStatus [ "temperature" ] = "NOK" ;
2024-05-07 16:28:43 +02:00
}
}
2024-09-09 12:32:34 +02:00
else if ( flowdata . data ? . values )
2024-05-21 15:49:42 +02:00
{
2024-09-09 12:32:34 +02:00
const values = flowdata . data . values ;
if ( values . hasOwnProperty ( "twilight_sensor" ) )
{
instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "lux_sensor" , value : values [ "twilight_sensor" ] } ) ;
2024-09-17 08:54:32 +02:00
deviceStatus [ "twilight_sensor" ] = "OK"
2024-09-09 12:32:34 +02:00
}
2024-09-17 08:54:32 +02:00
//"temperature" comes just from LM. Unipi handles thermometer from ws evok.
2024-09-09 12:32:34 +02:00
else if ( values . hasOwnProperty ( "temperature" ) )
{
2024-09-17 08:54:32 +02:00
previousValues [ "temperature" ] [ "lastTimeTemperatureReceived" ] = Date . now ( ) / 1000 ; //time in seconds
deviceStatus [ "temperature" ] = "OK" ;
2024-09-09 12:32:34 +02:00
}
// EM
else if ( values . hasOwnProperty ( "total_power" ) || values . hasOwnProperty ( "total_energy" ) || values . hasOwnProperty ( "power_factor" ) || values . hasOwnProperty ( "Phase_1_voltage" ) || values . hasOwnProperty ( "Phase_1_current" ) )
{
2024-09-17 08:54:32 +02:00
deviceStatus [ "em" ] = "OK" ;
2024-09-22 22:18:46 +02:00
FLOW . OMS _no _voltage . size > 0 ? deviceStatus [ "no_voltage" ] = "NOK" : deviceStatus [ "no_voltage" ] = "OK" ;
2024-09-09 12:32:34 +02:00
}
2024-05-07 16:28:43 +02:00
2024-09-09 12:32:34 +02:00
sendTelemetry ( values , FLOW . OMS _rvo _tbname ) ;
}
2024-05-07 16:28:43 +02:00
2024-09-22 22:18:46 +02:00
sendRvoStatus ( ) ;
2024-05-07 16:28:43 +02:00
} )
// we expect array as flowdata.data
instance . on ( "1" , flowdata => {
console . log ( flowdata . data ) ;
if ( ! flowdata . data instanceof Object ) return ;
let obj = flowdata . data ;
let line = obj . line ;
let force = obj . force ;
let info = obj . info ;
if ( obj . command == "turnOn" ) turnOnLine ( line , undefined , force , info ) ;
else if ( obj . command == "turnOff" ) turnOffLine ( line , undefined , force , info ) ;
else if ( obj . command == "turnOnAlarm" ) turnOnAlarm ( ) ;
else if ( obj . command == "turnOffAlarm" ) turnOffAlarm ( ) ;
//! ake data prichadzaju z cmd_manager.js ???
//TODO transform to websocket
if ( Array . isArray ( obj ) ) {
rsPort . write ( Buffer . from ( obj ) , function ( err ) {
switchLogic ( obj ) ;
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . debug , { "WRITE" : obj } ) ;
2024-05-07 16:28:43 +02:00
} ) ;
}
} )
function calculateStateCode ( )
{
let bytes = [ ] ;
let bits = [ ] ;
//Hlavný istič - state_of_main_switch
2024-09-22 22:18:46 +02:00
//TODO state_of main_switch is door contact in senica rvo - values should be "open" and "closed"
if ( deviceStatus [ "state_of_main_switch" ] == "closed" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 0 ) ;
}
2024-09-22 22:18:46 +02:00
else
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
//Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY
if ( ! FLOW . OMS _maintenance _mode )
{
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "rotary_switch_state" ] == "Manual" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 0 ) ;
bits . push ( 1 ) ;
}
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "rotary_switch_state" ] == "Automatic" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 0 ) ;
bits . push ( 0 ) ;
}
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "rotary_switch_state" ] == "Off" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
bits . push ( 0 ) ;
}
}
else {
bits . push ( 1 ) ;
bits . push ( 1 ) ;
}
//Dverový kontakt
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "door_condition" ] == "closed" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 0 ) ;
}
else
{
bits . push ( 1 ) ;
}
//EM
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "em" ] == "NOK" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
else
{
bits . push ( 0 ) ;
}
//Teplomer
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "temperature" ] == "NOK" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
else
{
bits . push ( 0 ) ;
}
//Batéria
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "battery" ] == "NOK" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
else
{
bits . push ( 0 ) ;
}
//Zdroj
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "power_supply" ] == "NOK" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
else
{
bits . push ( 0 ) ;
}
//MN
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "master_node" ] == "NOK" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
else
{
bits . push ( 0 ) ;
}
//výpadok napätia na fáze
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "no_voltage" ] == "NOK" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
else
{
bits . push ( 0 ) ;
}
2024-09-17 08:54:32 +02:00
if ( deviceStatus [ "twilight_sensor" ] == "NOK" )
2024-05-07 16:28:43 +02:00
{
bits . push ( 1 ) ;
}
else
{
bits . push ( 0 ) ;
}
// doplnime do 16 bitov (2 byty)
for ( let i = bits . length ; i < 16 ; i ++ )
{
bits . push ( 0 ) ;
}
2024-09-17 08:54:32 +02:00
// console.log("calculateStateCode - deviceStatus", deviceStatus);
2024-05-07 16:28:43 +02:00
// console.log("calculateStateCode", bits);
let byte0 = bitwise . byte . write ( bits . slice ( 0 , 8 ) . reverse ( ) ) ;
let byte1 = bitwise . byte . write ( bits . slice ( 8 ) . reverse ( ) ) ;
let byte = bytesToInt ( [ byte1 , byte0 ] ) ;
//console.log("calculateStateCode -------------------", byte);
return byte ;
}
2024-09-22 22:18:46 +02:00
async function sendRvoStatus ( ) {
2024-09-17 08:54:32 +02:00
const table = {
"OK" : 1 ,
"NOK" : 0
} ;
const dataToTb = {
"electrometer_status" : table [ deviceStatus [ "em" ] ] ,
"twilight_sensor_status" : table [ deviceStatus [ "twilight_sensor" ] ] ,
"thermometer_status" : table [ deviceStatus [ "temperature" ] ] ,
"phase_1_status" : 1 ,
"phase_2_status" : 1 ,
"phase_3_status" : 1 ,
"master_node_status" : table [ deviceStatus [ "master_node" ] ]
} ;
2024-09-22 22:18:46 +02:00
for ( const phase of FLOW . OMS _no _voltage ) dataToTb [ ` phase_ ${ phase } _status ` ] = 0 ;
2024-09-17 08:54:32 +02:00
//thermometer did not send data for more than a hour. Time in seconds
if ( deviceStatus [ "temperature" ] === "OK" )
{
2024-09-22 22:18:46 +02:00
if ( previousValues [ "temperature" ] [ "lastTimeTemperatureReceived" ] + TIME _AFTER _TEMPERATURE _NOK _STATUS < Date . now ( ) / 1000 )
2024-09-17 08:54:32 +02:00
{
2024-09-22 22:18:46 +02:00
// to be able to calculate proper RVO status, we need to await writeThermometerStatusToDb function
await writeThermometerStatusToDb ( "NOK" ) ;
2024-09-17 08:54:32 +02:00
dataToTb [ "thermometer_status" ] = 0 ;
}
}
2024-09-22 22:18:46 +02:00
dataToTb [ "status" ] = checkRvoStatus ( ) ;
dataToTb [ "statecode" ] = calculateStateCode ( ) ;
2024-09-17 08:54:32 +02:00
sendTelemetry ( dataToTb , FLOW . OMS _rvo _tbname ) ;
}
2024-09-22 22:18:46 +02:00
function checkRvoStatus ( ) {
2024-05-07 16:28:43 +02:00
// we check if any of these pins values are 0 --> it means status RVO is "NOK"
2024-09-17 08:54:32 +02:00
// pinIndex 6 is door_condition - if it is opened in maintenance mode - status = OK
2024-05-07 16:28:43 +02:00
//set RVO state
let status = "OK" ;
2024-09-22 22:18:46 +02:00
for ( const [ key , value ] of Object . entries ( deviceStatus ) ) {
if ( [ "em" , "twilight_sensor" , "temperature" ] . includes ( key ) && value == "NOK" ) status = "NOK" ;
2024-05-07 16:28:43 +02:00
}
if ( status == "OK" )
{
let pinIndexes = [ 1 , 4 , 6 ] ;
if ( controller _type == 'unipi' ) pinIndexes = [ 'input1_01' , 'input1_04' , 'input1_05' ] ;
for ( const pinIndex of pinIndexes ) {
if ( previousValues [ pinIndex ] === 0 ) {
if ( ( pinIndex === 6 || pinIndex === 'input1_01' || pinIndex === 'input1_05' ) && FLOW . OMS _maintenance _mode ) continue ;
status = "NOK" ;
break ;
}
}
}
// battery status. If value is 1 - battery is NOK
2024-09-22 22:18:46 +02:00
if ( previousValues [ 5 ] === 1 ) status = "NOK" ;
if ( ! FLOW . OMS _masterNodeIsResponding ) status = "NOK" ;
if ( FLOW . OMS _no _voltage . size > 0 ) status = "NOK" ;
2024-05-07 16:28:43 +02:00
2024-09-22 22:18:46 +02:00
return status ;
2024-05-07 16:28:43 +02:00
}
// we pass array to function in case of rsPort ==> switchLogic([55,3,0,1]) ==> [[55,3,0,1]]
// we pass two values in case of websocket ==> switchLogic("relay1_03",1) ==> ["relay1_03",1]
const switchLogic = ( ... args ) => {
2024-09-22 22:18:46 +02:00
let values = { } ;
let pinIndex , newPinValue , twilight ;
2024-05-07 16:28:43 +02:00
//data from rsPort
if ( args . length == 1 )
{
pinIndex = args [ 0 ] [ 1 ] + 1 ;
if ( pinIndex === 17 ) pinIndex -- ;
newPinValue = args [ 0 ] [ 3 ] ;
twilight = args [ 0 ] [ 2 ] ;
}
//data from websocket
else
{
pinIndex = args [ 0 ] ;
newPinValue = args [ 1 ] ;
}
let obj = pinsData [ pinIndex ] ;
if ( obj == undefined )
{
previousValues [ pinIndex ] = newPinValue ;
2024-09-22 22:18:46 +02:00
logger . debug ( "dido-switchLogic ==> no pinIndex" , pinIndex ) ;
2024-05-07 16:28:43 +02:00
return ;
}
2024-09-17 08:54:32 +02:00
//tbname is added to pinsData in initialSettings function
2024-05-07 16:28:43 +02:00
let type = obj . type ;
let line = obj . line ;
let tbname = obj . tbname ;
//default value
let value = "On" ;
if ( newPinValue === 0 ) value = "Off" ;
//Hlavný istič
//! po novom uz 'state of main switch' nemame. Namiesto neho je 'door_condition', kedze mame dvoje dveri
//! takze ked pride z evoku signal pre 'input1_05', handlujeme ho ako 'door_condition'
2024-09-22 22:18:46 +02:00
// if(type === "!!!state_of_main_switch")
// {
// if (newPinValue === 0 && newPinValue !== previousValues[pinIndex])
// {
// sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_off", {}, "", SEND_TO.tb, instance , "state_of_main_switch");
// values["status"] = "NOK";
// deviceStatus["state_of_main_switch"] = "Off";
// }
// else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex])
// {
// sendNotification("switchLogic", edgeName, "main_switch_has_been_turned_on", {}, "", SEND_TO.tb, instance , "state_of_main_switch");
// deviceStatus["state_of_main_switch"] = "On";
// }
// }
2024-05-07 16:28:43 +02:00
//Prevádzkový mód
if ( type == "rotary_switch_state" )
{
// combination of these two pins required to get result
let pin2 , pin3 ;
if ( pinIndex == 2 || pinIndex == "input1_02" )
{
pin2 = newPinValue ;
pin3 = previousValues [ 3 ] || previousValues [ "input1_03" ] ;
if ( pin3 == undefined )
{
previousValues [ pinIndex ] = newPinValue ;
return ;
}
}
else if ( pinIndex == 3 || pinIndex == "input1_03" )
{
pin3 = newPinValue ;
pin2 = previousValues [ 2 ] || previousValues [ "input1_02" ] ;
if ( pin2 == undefined )
{
previousValues [ pinIndex ] = newPinValue ;
return ;
}
}
//console.log('***********************', pin2, pin3)
if ( pin2 == 1 && pin3 == 0 ) value = "Manual" ;
if ( pin2 == 0 && pin3 == 0 ) value = "Off" ;
if ( pin2 == 0 && pin3 == 1 ) value = "Automatic" ;
2024-09-17 08:54:32 +02:00
deviceStatus [ "rotary_switch_state" ] = value ;
2024-05-07 16:28:43 +02:00
//automatic - profilu pre nody sa vykonavaju
//ak je spracovany, a automatic - tak ho zapnem
//ak nie je spracovany, iba profil zapisem
2024-09-22 22:18:46 +02:00
if ( pin2 != undefined && pin3 != undefined ) instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "rotary_switch_state" , value : value } ) ;
2024-05-07 16:28:43 +02:00
//console.log("rotary_switch_state pin", pin2, pin3, value);
}
//Zdroj - pin 4
else if ( type === "power_supply" )
{
if ( newPinValue === 0 && newPinValue !== previousValues [ pinIndex ] )
{
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ALERT, "Power supply is not OK", "", SEND_TO.tb, instance);
sendNotification ( "switchLogic" , edgeName , "power_supply_has_disconnected_input" , { } , "" , SEND _TO . tb , instance , "power_supply" ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
deviceStatus [ "power_supply" ] = "NOK" ;
2024-05-07 16:28:43 +02:00
}
else if ( newPinValue === 1 && newPinValue !== previousValues [ pinIndex ] )
{
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Power supply is is OK", "", SEND_TO.tb, instance);
sendNotification ( "switchLogic" , edgeName , "power_supply_works_correctly" , { } , "" , SEND _TO . tb , instance , "power_supply" ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
deviceStatus [ "power_supply" ] = "OK" ;
2024-05-07 16:28:43 +02:00
}
}
//Batéria - pin 5
else if ( type === "battery" )
{
if ( newPinValue === 1 && newPinValue !== previousValues [ pinIndex ] )
{
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Battery is not OK", "", SEND_TO.tb, instance);
sendNotification ( "switchLogic" , edgeName , "battery_level_is_low" , { } , "" , SEND _TO . tb , instance , "battery_level" ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
deviceStatus [ "battery" ] = "NOK" ;
2024-05-07 16:28:43 +02:00
}
else if ( newPinValue === 0 && newPinValue !== previousValues [ pinIndex ] )
{
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Battery is OK", "", SEND_TO.tb, instance);
sendNotification ( "switchLogic" , edgeName , "battery_level_is_ok" , { } , "" , SEND _TO . tb , instance , "battery_level" ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
deviceStatus [ "battery" ] = "OK" ;
2024-05-07 16:28:43 +02:00
}
}
//Dverový kontakt - pin 6
//! Po novom mame dva dverove kontakty, nie jeden. Druhy je teraz tam, kde bol digital input "state_of_main_switch"
//! preto ked pride z evoku signal z input1_05, co bol predytm "main switch" handlujeme ho teraz ako 'door_condition'
else if ( type == "door_condition" || type === "state_of_main_switch" )
{
newPinValue === 0 ? value = "open" : value = "closed" ;
if ( value === "open" && FLOW . OMS _maintenance _mode )
{
2024-09-09 12:32:34 +02:00
sendNotification ( "switchLogic" , edgeName , "door_has_been_open" , { } , "" , SEND _TO . tb , instance , "rvo_door" ) ;
2024-05-07 16:28:43 +02:00
}
if ( value === "open" && ! FLOW . OMS _maintenance _mode )
{
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.WARNING, "RVO open door out of maintenance mode", "", SEND_TO.tb, instance);
sendNotification ( "switchLogic" , edgeName , "door_has_been_open_without_permision_alarm_is_on" , { } , "" , SEND _TO . tb , instance , "rvo_door" ) ;
2024-05-07 16:28:43 +02:00
// zapneme sirenu
// ak sa otvoria dvere len na elektromeri (type === "state_of_main_switch") alarm sa nema spustit. alarm sa spusti len ked sa otvoria hlavne dvere (type === "door_condition")
if ( type === "door_condition" ) turnOnAlarm ( ) ;
}
if ( value === "closed" )
{
if ( alarmStatus == "ON" ) turnOffAlarm ( ) ;
//turnOffAlarm();
2024-09-09 12:32:34 +02:00
sendNotification ( "switchLogic" , edgeName , "door_has_been_closed" , { } , "" , SEND _TO . tb , instance , "rvo_door" ) ;
2024-05-07 16:28:43 +02:00
}
2024-09-22 22:18:46 +02:00
deviceStatus [ type ] = value ;
2024-05-07 16:28:43 +02:00
}
//lux sensor
else if ( type == "twilight_sensor" )
{
2024-09-17 08:54:32 +02:00
//! TODO - to show nok status, if lux value is not changing more then 10 times.
2024-05-07 16:28:43 +02:00
//Daylight is far more than 1000. So most of the day, when it is sunshine comes just value 1000. But lux sensor is not NOK.
//This is not the case in LM. If value from LM is the same 10x, there is 99% possibility, that sensor is NOK.
value = newPinValue ;
if ( controller _type === 'lm' )
{
value = parseFloat ( newPinValue + ( 256 * twilight ) ) ;
let now = new Date ( ) ;
//new Date(dusk.getTime()
let obj = { timestamp : now . getTime ( ) , value : value } ;
//test
//twilight_sensor_interval = 1;
twilight _sensor . push ( obj ) ;
//twilight_sensor_array.push(value);
//check if we receive just 1 constant value from lux sensor ==> error
if ( twilight _sensor _array . length > 10 ) {
let set = new Set ( twilight _sensor _array ) ;
if ( set . size === 1 && ! twilightError )
{
twilightError = true ;
let value = twilight _sensor _array . shift ( ) ;
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.ERROR, "Lux sensor error", {"Repeating value": value}, SEND_TO.tb, instance );
2024-05-07 16:28:43 +02:00
newPinValue = 0 ;
}
else if ( set . size !== 1 && twilightError )
{
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance );
2024-05-07 16:28:43 +02:00
twilightError = false ;
twilight _sensor _array . shift ( ) ;
newPinValue = value ;
}
else if ( set . size === 1 && twilightError )
{
twilight _sensor _array . shift ( ) ;
newPinValue = 0 ;
}
}
let diff = twilight _sensor [ twilight _sensor . length - 1 ] . timestamp - twilight _sensor [ 0 ] . timestamp ;
if ( diff >= twilight _sensor _interval * 60 * 1000 )
{
const average = twilight _sensor . reduce ( ( acc , c ) => acc + c . value , 0 ) / twilight _sensor . length ;
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "lux_sensor" , value : average } ) ;
2024-05-07 16:28:43 +02:00
twilight _sensor = [ ] ;
//console.log("lux_sensor send", average);
}
//else console.log("lux_sensor", value, diff);
}
}
else if ( type == "state_of_contactor" )
{
2024-09-09 12:32:34 +02:00
//sendNotification("switchLogic", edgeName, ERRWEIGHT.INFO, `State of contactor ${line} is now ${value}`, "", SEND_TO.tb, instance );
2024-09-17 08:54:32 +02:00
if ( ! ( deviceStatus [ "state_of_contactor" ] [ line ] == value ) )
2024-05-07 16:28:43 +02:00
{
2024-09-09 12:32:34 +02:00
sendNotification ( "switchLogic" , edgeName , "state_of_contactor_for_line" , { line : line , value : value } , "" , SEND _TO . tb , instance ) ;
2024-05-07 16:28:43 +02:00
}
2024-09-17 08:54:32 +02:00
deviceStatus [ "state_of_contactor" ] [ line ] = value ;
2024-05-07 16:28:43 +02:00
//true, false
if ( value === "On" ) value = true ;
2024-09-17 08:54:32 +02:00
else if ( value === "Off" ) value = false ;
2024-05-07 16:28:43 +02:00
//modify table relays
dbRelays . modify ( { contactor : newPinValue } ) . where ( "line" , line ) . make ( function ( builder ) {
builder . callback ( function ( err , response ) {
2024-09-17 08:54:32 +02:00
if ( ! err )
2024-05-07 16:28:43 +02:00
{
2024-09-17 08:54:32 +02:00
let time = 0 ;
if ( value ) time = 1000 * 10 ; //10 sekund
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
let dataChanged = false ;
if ( relaysData [ line ] . contactor != value ) dataChanged = true ;
relaysData [ line ] . contactor = value ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
//ak bola predchadzajuci stav off a novy stav je on, budu sa nastavovat nespracovane node profiles
//a budu sa odosielat commandy, tie vsak mozu zlyhat, a preto potrebujeme ich spusti trochu neskor
setTimeout ( function ( ) {
instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "reload_relays" , line : line , time : time , value : value , dataChanged : dataChanged } ) ;
} , time ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
reportLineStatus ( line ) ;
}
else
{
errLogger . error ( "modify table relays failed" , err ) ;
}
2024-05-07 16:28:43 +02:00
} ) ;
2024-09-17 08:54:32 +02:00
} ) ;
2024-05-07 16:28:43 +02:00
}
2024-09-22 22:18:46 +02:00
else if ( type === "state_of_breaker" )
2024-05-07 16:28:43 +02:00
{
let valueChanged = false ;
if ( newPinValue != previousValues [ pinIndex ] ) valueChanged = true ;
if ( valueChanged )
{
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "state_of_breaker" , value : value , line : line } ) ;
2024-05-07 16:28:43 +02:00
//mame iba 3 istice. vyreportujeme a ohandlujeme liniu na tom istom istici ako paralelna linia (napr linia 1, paralelna s nou je linia 4, key je string "4")
// ak je 7 linii, na 1 istici je linia 1,4,7
if ( line == 1 )
{
const lineOnSameBraker = [ 4 , 7 ] ;
for ( var i = 0 ; i < lineOnSameBraker . length ; i ++ )
{
if ( ! relaysData . hasOwnProperty ( lineOnSameBraker [ i ] ) ) continue ;
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "state_of_breaker" , value : value , line : lineOnSameBraker [ i ] } ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
deviceStatus [ "state_of_breaker" ] [ lineOnSameBraker [ i ] ] = value ;
2024-05-07 16:28:43 +02:00
reportLineStatus ( lineOnSameBraker [ i ] ) ;
values [ type ] = value ;
const tbname = relaysData [ lineOnSameBraker [ i ] ] . tbname ;
sendTelemetry ( values , tbname ) ;
delete values [ type ] ;
}
}
else
{
const lineOnSameBraker = line + 3 + "" ;
if ( relaysData . hasOwnProperty ( lineOnSameBraker ) )
{
2024-09-09 12:32:34 +02:00
instance . send ( SEND _TO . cmd _manager , { sender : "dido_controller" , cmd : "state_of_breaker" , value : value , line : line + 3 } ) ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
deviceStatus [ "state_of_breaker" ] [ line + 3 ] = value ;
2024-05-07 16:28:43 +02:00
reportLineStatus ( line + 3 ) ;
values [ type ] = value ;
const tbname = relaysData [ lineOnSameBraker ] . tbname ;
sendTelemetry ( values , tbname ) ;
delete values [ type ] ;
}
}
}
if ( value == "Off" ) values [ "status" ] = "NOK" ;
2024-09-17 08:54:32 +02:00
deviceStatus [ "state_of_breaker" ] [ line ] = value ;
2024-05-07 16:28:43 +02:00
reportLineStatus ( line ) ;
}
2024-09-22 22:18:46 +02:00
else return ;
2024-05-07 16:28:43 +02:00
values [ type ] = value ;
2024-09-22 22:18:46 +02:00
if ( type == "rotary_switch_state" )
2024-05-07 16:28:43 +02:00
{
2024-09-22 22:18:46 +02:00
if ( FLOW . OMS _maintenance _mode ) value = "maintenance" ;
value = value . toLowerCase ( ) ;
values [ "power_mode" ] = value ;
2024-05-07 16:28:43 +02:00
}
2024-09-22 22:18:46 +02:00
if ( newPinValue != previousValues [ pinIndex ] ) previousValues [ pinIndex ] = newPinValue ;
if ( FLOW . OMS _rvo _tbname == tbname ) sendRvoStatus ( ) ;
if ( Object . keys ( values ) . length > 0 && tbname ) sendTelemetry ( values , tbname ) ;
2024-05-07 16:28:43 +02:00
}
2024-09-17 08:54:32 +02:00
function sendTelemetry ( values , tbname ) {
2024-05-07 16:28:43 +02:00
let dataToTb = {
[ tbname ] : [
{
"ts" : Date . now ( ) ,
"values" : values
}
]
2024-09-17 08:54:32 +02:00
} ;
2024-05-07 16:28:43 +02:00
2024-09-17 08:54:32 +02:00
// instance.send(SEND_TO.tb, dataToTb);
tbHandler . sendToTb ( dataToTb , instance ) ;
2024-05-07 16:28:43 +02:00
}
2024-09-17 08:54:32 +02:00
function writeThermometerStatusToDb ( status ) {
2024-09-22 22:18:46 +02:00
return new Promise ( function ( resolve , reject ) {
dbStatus . modify ( { thermometer : status } ) . make ( function ( builder ) {
builder . callback ( function ( err , response ) {
if ( ! err )
{
deviceStatus [ "temperature" ] = status ;
console . log ( ` Wrote to db status: thermometer ${ status } ` ) ;
resolve ( "ok" )
}
reject ( "nok" )
} ) ;
} ) ;
} )
2024-09-17 08:54:32 +02:00
}
function isObject ( item ) {
return ( typeof item === "object" && ! Array . isArray ( item ) && item !== null ) ;
}
} //end of instance
2024-05-07 16:28:43 +02:00
//! incomming data to websocket
// [
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_08',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_01',
// alias: 'al_lights_kitchen',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_02',
// alias: 'al_lights_bedroom',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_03',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_04',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_05',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_06',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// glob_dev_id: 1,
// modes: [ 'Simple' ],
// value: 0,
// circuit: '1_07',
// pending: false,
// relay_type: 'physical',
// dev: 'relay',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_08',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_mode: 'Enabled',
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// dev: 'input',
// modes: [ 'Simple', 'DirectSwitch' ],
// debounce: 50,
// counter: 1,
// value: 1,
// alias: 'al_main_switch',
// mode: 'Simple',
// circuit: '1_01'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 1,
// circuit: '1_02',
// debounce: 50,
// counter: 2,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 1,
// circuit: '1_03',
// debounce: 50,
// counter: 2,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_04',
// debounce: 50,
// counter: 1,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_05',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_06',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// counter_modes: [ 'Enabled', 'Disabled' ],
// glob_dev_id: 1,
// modes: [ 'Simple', 'DirectSwitch' ],
// value: 0,
// circuit: '1_07',
// debounce: 50,
// counter: 0,
// counter_mode: 'Enabled',
// dev: 'input',
// mode: 'Simple'
// },
// {
// interval: 3,
// value: 24.5,
// circuit: '28744F7791180257',
// address: '28744F7791180257',
// time: 1631873896.48797,
// typ: 'DS18B20',
// lost: false,
// dev: 'temp'
// },
// {
// bus: '/dev/i2c-2',
// interval: 3,
// dev: 'owbus',
// scan_interval: 300,
// circuit: '1',
// do_scan: false,
// do_reset: false
// },
// {
// glob_dev_id: 1,
// last_comm: 0.014672994613647461,
// ver2: '0.1',
// sn: 42,
// circuit: '1',
// model: 'S207',
// dev: 'neuron',
// board_count: 1
// },
// {
// circuit: '1_01',
// value: 0,
// glob_dev_id: 1,
// dev: 'wd',
// timeout: 5000,
// was_wd_reset: 0,
// nv_save: 0
// }
// ]
//! loaded pins_data --> from LM
// {
// '1': { pin: 1, type: 'state_of_main_switch', line: 0 },
// '2': { pin: 2, type: 'rotary_switch_state', line: 0 },
// '3': { pin: 3, type: 'rotary_switch_state', line: 0 },
// '4': { pin: 4, type: 'power_supply', line: 0 },
// '5': { pin: 5, type: 'battery', line: 0 },
// '6': { pin: 6, type: 'door_condition', line: 0 },
// '8': { pin: 8, type: 'state_of_breaker', line: 1 },
// '9': { pin: 9, type: 'state_of_breaker', line: 2 },
// '10': { pin: 10, type: 'state_of_breaker', line: 3 },
// '11': { pin: 11, type: 'state_of_contactor', line: 1 },
// '12': { pin: 12, type: 'state_of_contactor', line: 2 },
// '13': { pin: 13, type: 'state_of_contactor', line: 3 },
// '16': { pin: 16, type: 'twilight_sensor', line: 0 }
// }
//! pins.table --> from LM
// pin:number|type:string|line:number
// *|1|state_of_main_switch|0|...........
// *|2|rotary_switch_state|0|...........
// *|3|rotary_switch_state|0|...........
// *|4|power_supply|0|...........
// *|5|battery|0|...........
// *|6|door_condition|0|...........
// *|8|state_of_breaker|1|...........
// *|9|state_of_breaker|2|...........
// *|10|state_of_breaker|3|...........
// *|11|state_of_contactor|1|...........
// *|12|state_of_contactor|2|...........
// *|13|state_of_contactor|3|...........
// *|16|twilight_sensor|0|...........
//! pins.table --> from UNIPI
// pin:string|type:string|line:number
// *|input1_01|state_of_main_switch|0|...........
// *|input1_02|rotary_switch_state|0|...........
// *|input1_03|rotary_switch_state|0|...........
// *|intut1_04|power_supply|0|...........
// *|input1_05|door_condition|0|...........
// *|input1_06|state_of_breaker|1|...........
// *|input1_07|state_of_breaker|2|...........
// *|input1_08|state_of_breaker|3|...........
// *|relay1_02|state_of_contactor|1|...........
// *|relay1_03|state_of_contactor|2|...........
// *|relay1_04|state_of_contactor|3|...........
// *|287D8776E0013CE9|temperature|0|...........
//! pins_data --> from UNIPI
// {
2024-09-22 22:18:46 +02:00
// input1_01: {
// pin: 'input1_01',
// type: 'door_condition',
// line: 0,
// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8'
// },
// input1_02: {
// pin: 'input1_02',
// type: 'rotary_switch_state',
// line: 0,
// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8'
// },
// input1_03: {
// pin: 'input1_03',
// type: 'rotary_switch_state',
// line: 0,
// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8'
// },
// input1_04: {
// pin: 'input1_04',
// type: 'power_supply',
// line: 0,
// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8'
// },
// input1_05: {
// pin: 'input1_05',
// type: 'state_of_main_switch',
// line: 0,
// tbname: 'PLBJzmK1r3Gynd6OW0gGYz0e5wV4vx9bDEqNgYR8'
// },
// input1_06: {
// pin: 'input1_06',
// type: 'state_of_breaker',
// line: 1,
// tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo'
// },
// relay1_02: {
// pin: 'relay1_02',
// type: 'state_of_contactor',
// line: 1,
// tbname: '52dD6ZlV1QaOpRBmbAqK8bkKnGzWMLj4eJq38Pgo'
// },
// '28F46E9D0E00008B': { pin: '28F46E9D0E00008B', type: 'temperature', line: 0 },
// twilight_sensor: { pin: 'twilight_sensor', type: 'twilight_sensor', line: 0 }
2024-05-07 16:28:43 +02:00
// }
//! relays_data
// {
// '0': {
// line: 0,
// tbname: 'KjbN4q7JPZmexgdnz2yKQ98YAWwO0Q3BMX6ERLoV',
// contactor: 1,
// profile: ''
// },
// '1': {
// line: 1,
// tbname: 'RMgnK93rkoAazbqdQ4yBG95Z1YXGx6pmwBeVEP2O',
// contactor: 0,
// profile: '{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"10:00","start_time":"20:00"},{"value":0,"end_time":"10:20","start_time":"10:00"},{"value":1,"end_time":"10:40","start_time":"10:20"},{"value":0,"end_time":"11:00","start_time":"10:40"},{"value":1,"end_time":"11:30","start_time":"11:00"},{"value":0,"end_time":"11:50","start_time":"11:30"},{"value":1,"end_time":"13:00","start_time":"11:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}'
// },
// '2': {
// line: 2,
// tbname: 'dlE1VQjYrNx9gZRmb38gG08oLBO4qaAk2M6JPnG7',
// contactor: 0,
// profile: '{"intervals":[{"value":0,"end_time":"20:00","start_time":"13:00"},{"value":1,"end_time":"10:00","start_time":"20:00"},{"value":0,"end_time":"10:20","start_time":"10:00"},{"value":1,"end_time":"10:40","start_time":"10:20"},{"value":0,"end_time":"11:00","start_time":"10:40"},{"value":1,"end_time":"11:30","start_time":"11:00"},{"value":0,"end_time":"11:50","start_time":"11:30"},{"value":1,"end_time":"13:00","start_time":"11:50"}],"astro_clock":false,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}'
// },
// '3': {
// line: 3,
// tbname: 'vnmG4kJxaXWNBgMQq0D7Aj5e9oZzOAlr6LdR3w2V',
// contactor: 0,
// profile: '{"intervals":[{"value":0,"end_time":"20:30","start_time":"13:00"},{"value":1,"end_time":"00:10","start_time":"20:30"},{"value":0,"end_time":"13:00","start_time":"05:40"},{"value":1,"end_time":"05:40","start_time":"00:10"}],"astro_clock":true,"dawn_lux_sensor":false,"dusk_lux_sensor":false,"dawn_lux_sensor_value":5,"dusk_lux_sensor_value":5,"dawn_astro_clock_offset":0,"dusk_astro_clock_offset":0,"dawn_lux_sensor_time_window":30,"dusk_lux_sensor_time_window":30,"dawn_astro_clock_time_window":60,"dusk_astro_clock_time_window":60}'
// }
// }
// {
// "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV": [
// {
// "ts": 1700409326353,
// "values": {
// "_event": {
// "type": "notice",
// "status": "new",
// "source": {
// "func": "rsPort.open()",
// "component": "1700343402190",
// "component_name": "DIDO_Controller",
// "edge": "jbN4q7JPZmexgdnz2yKbWGDYAWwO0Q3BMX6ERLoV"
// },
// "message": "al_shariff_10.0.0.38: FLOW has been started ",
// "message_data": ""
// }
// }
// }
// ]
// }