exports.id = 'dido_controller';
exports.title = 'DIDO_Controller';
exports.version = '2.0.0';
exports.group = 'Worksys';
exports.color = '#2134B0';
exports.input = 3;
exports.output = ["red", "white", "yellow", "green"];
exports.click = false;
exports.icon = 'bolt';
exports.options = { edge: "undefined" };

exports.html = `<div class="padding">
  <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</div>
    </div>
  </div>
</div>`;

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

state_of_main_switch  -  reportovať stav hlavného ističa : 0-> off  1-> on  
rotary_switch_state   - sem by sa mal reportovať stav vstupov manual a auto podľa nasledovnej logiky: Manual = 1  a Auto = 0  -> Manual,
Manual = 0  a Auto = 0  -> Off, Manual = 0  a Auto = 1  -> Automatic

door_condition - 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

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
*/

const { errLogger, logger, monitor } = require('./helper/logger');
const SerialPort = require('serialport');
const WebSocket = require('ws');
const { runSyncExec } = require('./helper/serialport_helper');
const { bytesToInt, resizeArray } = require('./helper/utils');
const { sendNotification } = require('./helper/notification_reporter');
const bitwise = require('bitwise');

const DataToTbHandler = require('./helper/DataToTbHandler');
let tbHandler;

const ErrorToServiceHandler = require('./helper/ErrorToServiceHandler');
const errorHandler = new ErrorToServiceHandler();

let ws = null;
let rsPort = null;

let pinsData;
let relaysData;
let rvoTbName;
let GLOBALS; //FLOW global GLOBALS
let SETTINGS; // GLOBALS.settings
let controller_type;

let alarmStatus = "OFF";

const SEND_TO = {
  debug: 0,
  tb: 1,
  cmd_manager: 2
}


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);
  })

  let previousValues = {};
  let rsPortReceivedData = [];

  //to be able to get proper twilight values
  let twilight_sensor_interval = 5;//minutes
  let twilight_sensor = [];
  const twilight_sensor_array = [];
  let twilightError = false;

  monitor.info("DIDO_Relay_Controller installed");

  //key is PIN number , line: 0 = RVO
  /*
  let conversionTable = {
    "1": {tbname: "", type: "state_of_main_switch", "line": 0}, //state_of_main_switch  pin1
    "2": {tbname: "", type: "rotary_switch_state", "line": 0}, //rotary_switch_state  - poloha manual = pin2
    "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
  };
  */

  //status for calculating Statecodes
  let deviceStatus = {                    //key is device name: temperature,....
    "state_of_main_switch": "Off",         //Hlavný istič (po novom druhy dverovy kontakt)
    "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 - GLOBALS.settings.masterNodeIsResponding
    "no_voltage": "OK",                    //GLOBALS.settings.no_voltage - výpadok napätia na fáze
    "state_of_breaker": {},                //"Off",//Istič
    "state_of_contactor": {},              //"Off",//Stykač
    "twilight_sensor": "OK"                //lux sensor
  };


  function main() {

    GLOBALS = FLOW.GLOBALS;
    SETTINGS = FLOW.GLOBALS.settings;
    rvoTbName = SETTINGS.rvoTbName;
    pinsData = GLOBALS.pinsData;
    relaysData = GLOBALS.relaysData;

    tbHandler = new DataToTbHandler(SEND_TO.tb)
    tbHandler.setSender(exports.title);

    controller_type = SETTINGS.controller_type //"lm" or "unipi" //logicMachine
    if (controller_type == "") controller_type = "lm";

    console.log(exports.title, "controller type: ", controller_type);

    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 

    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);
          sendNotification("set port ", rvoTbName, "local_database_is_corrupted", {}, "", SEND_TO.tb, instance);
        }
      }

      if (pinsData[key].type == "state_of_contactor") {
        let pin = key - 1;
        if (controller_type === "unipi") pin = key;
      }
    }

    //report RVO version relaysData[0].tbname;
    let values = {};
    values["edge_fw_version"] = SETTINGS.edge_fw_version;
    values["maintenance_mode"] = SETTINGS.maintenance_mode;

    sendTelemetry(values, rvoTbName);

    instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "buildTasks" });

    sendNotification("rsPort.open()", rvoTbName, "flow_start", {}, "", SEND_TO.tb, instance);
    monitor.info("-->FLOW bol spustený", rvoTbName, SETTINGS.edge_fw_version);
  }


  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);
      instance.send(SEND_TO.debug, err.message);

      errorHandler.sendMessageToService(exports.title + " rsPort opened error - failed: " + err.message);
    })

    rsPort.on('open', async function() {

      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) {

        //set port
        rsPort.write(Buffer.from(setRSPortData), function(err) {
          if (!err) {
            monitor.info(exports.title + "--->Digital in_out has been set (runSyncExec was sucessfull)");

            turnAlarm("off");
            initialSetting();
          }
        })

      }).catch(function(reason) {
        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");
      turnAlarm("off");

      initialSetting();

      setTimeout(function() { ws.send(JSON.stringify({ cmd: "all" })) }, 5000);
      // 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
      startRequests = setInterval(() => {
        ws.send(JSON.stringify({ "cmd": "filter", "dev": ["neuron"] }));
      }, 150000)
    };


    // 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'
    //   },
    ws.onmessage = async function(data) {

      data = JSON.parse(data.data);

      // data comes in array except of "temperature" ==> it comes as an object
      // we do not handle temperature from evok any more => we return, if temperature comes:
      if (isObject(data)) 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) => {
      monitor.info('websocket error, reconnect')
      instance.send(SEND_TO.debug, err.message);
      clearInterval(startRequests);
      ws = null;
      setTimeout(handleWebSocket, 1000);
    })


    ws.onclose = function() {
      // connection closed, discard old websocket and create a new one in 5s
      // stopRequests();
      monitor.info('websocket onclose, reconnect')
      clearInterval(startRequests);
      ws = null;
      console.log("ws is null now, reconnecting...");
      setTimeout(handleWebSocket, 1000);
    }
  }

  instance.on("close", () => {
    if (rsPort) rsPort.close();
    if (ws) ws.close();
  })


  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 turnAlarm(onOrOff) {
    let value = 0;
    if (onOrOff == "on") value = 1;

    if (value == 1 && SETTINGS.maintenance_mode) return;

    alarmStatus = "OFF";
    if (value == 1) alarmStatus = "ON";

    if (rsPort) {
      let arr = [0x55];
      arr.push(13);
      arr.push(0);
      arr.push(value);

      rsPort.write(Buffer.from(arr), function(err) {
        logger.debug(`sirena - ${onOrOff}`);
      });
    }
    else if (ws) {
      let cmd = { "cmd": "set", "dev": "relay", "circuit": "1_01", "value": value };
      ws.send(JSON.stringify(cmd));
      logger.debug(`sirena - ${onOrOff}`);
    }
  }


  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 = [];

    if (deviceStatus["state_of_breaker"][line] == "On") {
      bits.push(0);
    }
    else bits.push(1);

    if (deviceStatus["state_of_contactor"][line] == "On") {
      bits.push(0);
    }
    else bits.push(1);

    resizeArray(bits, 8, 0);

    let byte = bitwise.byte.write(bits.reverse());

    //console.log("line", line, bits, byte);
    sendTelemetry({ statecode: byte }, tbname);
  }


  // turn line on or off
  function turnLine(onOrOff, line, pin, force, info) {
    //onOrOff => "on" or "off"
    let value = 0;
    if (onOrOff == "on") value = 1;

    if (force == undefined) force = false;

    if (line == 0) {
      if (value == 1 && alarmStatus == "ON") turnAlarm("off");
      SETTINGS.maintenance_mode = value ? true : false;

      let values = {};
      values["statecode"] = calculateStateCode();
      values["power_mode"] = value ? "maintenance" : "Automatic";
      sendTelemetry(values, rvoTbName);

      monitor.info(`turnLine ${onOrOff} - (line, SETTINGS.maintenance_mode)`, line, SETTINGS.maintenance_mode, info);
      return;
    }

    if (pin === undefined) pin = getPin(line);

    if (pin === undefined) {
      errLogger.error("pin is undefined!", line);
      return;
    }

    if (!force) {
      if (relaysData[line].contactor == value) {
        instance.send(SEND_TO.debug, `line is already ${onOrOff} ` + line);
        logger.debug(`turnLine: line is already ${onOrOff} `, line);
        return;
      }
    }

    // if(!rsPort.isOpen && !ws)
    if (!rsPort && !ws) {
      errLogger.error("dido controller - port or websocket is not opened");
      return;
    }

    if (rsPort) {
      let arr = [0x55];
      arr.push(pin);
      arr.push(0);
      arr.push(value);

      rsPort.write(Buffer.from(arr), function(err) {
        if (err === undefined) {
          monitor.info(`turnLine ${onOrOff} zapisal do rsPort-u`, line, pin, arr, info);
          switchLogic(arr);
        }
        else {
          monitor.info(`turnLine ${onOrOff} WRITE error`, err);
        }
      });

    }
    else if (ws) {
      //pin = "relay1_03" or "input1_01" ... we must make just "1_01" with slice method
      monitor.info(`turnLine ${onOrOff} - (line, pin, force)`, line, pin, force, info);
      let cmd = { "cmd": "set", "dev": "relay", "circuit": pin.slice(5), "value": value };
      ws.send(JSON.stringify(cmd));
    }

    //if rvo is 24/7, it has just one switching profile point at 13:00. we do not want to send notification as it repeats every day.
    //const d = new Date();
    //if(d.getHours() != 13) sendNotification("Dido_controller: ", SETTINGS.rvoTbName, "switching_profile_point_applied_to_line", { line: line, value: onOrOff }, "", SEND_TO.tb, instance);
  }


  // main opening
  instance.on("2", _ => {
    main();
  })


  //data from modbus_reader or temperature sensor or twilight sensor or other modbus device
  instance.on("0", flowdata => {

    if (!isObject(flowdata.data)) return;

    // console.log('***********************', flowdata.data)
    instance.send(SEND_TO.debug, flowdata.data);

    // we handle nok status from modbus_reader component and thermometer
    if ("status" in flowdata.data) {
      const status = flowdata.data.status;
      if (status == "NOK-twilight_sensor") {
        deviceStatus["twilight_sensor"] = "NOK";
      }
      else if (status == "NOK-em340" || status == "NOK-em111") {
        deviceStatus["em"] = "NOK";
      }
      else if (status == "NOK-thermometer") {
        deviceStatus["temperature"] = "NOK";
      }
    }
    else if ("values" in flowdata.data) {
      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"] });
        deviceStatus["twilight_sensor"] = "OK"
      }
      else if (values.hasOwnProperty("temperature")) {
        deviceStatus["temperature"] = "OK";
      }
      // 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")) {
        deviceStatus["em"] = "OK";
        SETTINGS.no_voltage.size > 0 ? deviceStatus["no_voltage"] = "NOK" : deviceStatus["no_voltage"] = "OK";
      }

      sendTelemetry(values, rvoTbName);
    }

    sendRvoStatus();
  })




  // 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 == "on") turnLine("on", line, undefined, force, info);
    else if (obj.command == "off") turnLine("off", line, undefined, force, info);
    else if (obj.command == "turnOnAlarm") turnAlarm("on");
    else if (obj.command == "turnOffAlarm") turnAlarm("off");
  })


  function calculateStateCode() {

    let bits = [];

    //Hlavný istič - state_of_main_switch => v rvo senica je to druhy door pre silovu cast (EM)
    if (deviceStatus["state_of_main_switch"] == "closed") {
      bits.push(0);
    }
    else {
      bits.push(1);
    }

    //Prevádzkový mód - Manual, Off, Automatic, maintenance_mode = true/false // DAVA 2 BITY
    if (!SETTINGS.maintenance_mode) {
      if (deviceStatus["rotary_switch_state"] == "Manual") {
        bits.push(0);
        bits.push(1);
      }

      if (deviceStatus["rotary_switch_state"] == "Automatic") {
        bits.push(0);
        bits.push(0);
      }

      if (deviceStatus["rotary_switch_state"] == "Off") {
        bits.push(1);
        bits.push(0);
      }
    }
    else {
      bits.push(1);
      bits.push(1);
    }

    //Dverový kontakt
    if (deviceStatus["door_condition"] == "closed") {
      bits.push(0);
    }
    else {
      bits.push(1);
    }

    //EM
    if (deviceStatus["em"] == "NOK") {
      bits.push(1);
    }
    else {
      bits.push(0);
    }

    //Teplomer
    if (deviceStatus["temperature"] == "NOK") {
      bits.push(1);
    }
    else {
      bits.push(0);
    }

    //Batéria
    if (deviceStatus["battery"] == "NOK") {
      bits.push(1);
    }
    else {
      bits.push(0);
    }

    //Zdroj
    if (deviceStatus["power_supply"] == "NOK") {
      bits.push(1);
    }
    else {
      bits.push(0);
    }

    //MN
    if (deviceStatus["master_node"] == "NOK") {
      bits.push(1);
    }
    else {
      bits.push(0);
    }

    //výpadok napätia na fáze
    if (deviceStatus["no_voltage"] == "NOK") {
      bits.push(1);
    }
    else {
      bits.push(0);
    }

    if (deviceStatus["twilight_sensor"] == "NOK") {
      bits.push(1);
    }
    else {
      bits.push(0);
    }

    // doplnime do 16 bitov (2 byty)
    for (let i = bits.length; i < 16; i++) {
      bits.push(0);
    }

    // console.log("calculateStateCode - deviceStatus", deviceStatus);
    // 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;
  }


  async function sendRvoStatus() {

    if (SETTINGS === undefined) return;
    SETTINGS.masterNodeIsResponding ? deviceStatus["master_node"] = "OK" : deviceStatus["master_node"] = "NOK";

    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"]]
    };

    for (const phase of SETTINGS.no_voltage) dataToTb[`phase_${phase}_status`] = 0;

    dataToTb["status"] = checkRvoStatus();
    dataToTb["statecode"] = calculateStateCode();

    //console.log(dataToTb);
    sendTelemetry(dataToTb, rvoTbName);
  }


  function checkRvoStatus() {

    // we check if any of these pins values are 0 --> it means status RVO is "NOK"
    // pinIndex 6 is door_condition - if it is opened in maintenance mode - status = OK
    //set RVO state
    let status = "OK";

    for (const [key, value] of Object.entries(deviceStatus)) {
      if (["em", "twilight_sensor", "temperature", "master_node"].includes(key) && value == "NOK") status = "NOK";
    }

    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') && SETTINGS.maintenance_mode) continue;
          status = "NOK";
          break;
        }
      }
    }

    // battery status. If value is 1 - battery is NOK
    if (previousValues[5] === 1) status = "NOK";
    if (SETTINGS.no_voltage.size > 0) status = "NOK";

    // console.log("rvo status",status)
    return status;
  }


  // 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) => {

    let values = {};
    let pinIndex, newPinValue, twilight;

    //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;
      //logger.debug("dido-switchLogic ==> no pinIndex", pinIndex);
      return;
    }

    //tbname is added to pinsData in initialSettings function
    let type = obj.type;
    let line = obj.line;
    let tbname = obj.tbname;

    //default value
    let value = "On";
    if (newPinValue === 0) value = "Off";

    //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";

      deviceStatus["rotary_switch_state"] = value;

      //automatic - profilu pre nody sa vykonavaju
      //ak je spracovany, a automatic - tak ho zapnem
      //ak nie je spracovany, iba profil zapisem

      if (pin2 != undefined && pin3 != undefined) instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "rotary_switch_state", value: value });

      //console.log("rotary_switch_state pin", pin2, pin3, value);
    }

    //Zdroj - pin 4
    else if (type === "power_supply") {
      if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) {
        sendNotification("switchLogic", rvoTbName, "power_supply_has_disconnected_input", {}, "", SEND_TO.tb, instance, "power_supply");

        deviceStatus["power_supply"] = "NOK";
      }
      else if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) {
        sendNotification("switchLogic", rvoTbName, "power_supply_works_correctly", {}, "", SEND_TO.tb, instance, "power_supply");

        deviceStatus["power_supply"] = "OK";
      }
    }

    //Batéria - pin 5
    else if (type === "battery") {
      if (newPinValue === 1 && newPinValue !== previousValues[pinIndex]) {
        sendNotification("switchLogic", rvoTbName, "battery_level_is_low", {}, "", SEND_TO.tb, instance, "battery_level");

        deviceStatus["battery"] = "NOK";
      }
      else if (newPinValue === 0 && newPinValue !== previousValues[pinIndex]) {
        sendNotification("switchLogic", rvoTbName, "battery_level_is_ok", {}, "", SEND_TO.tb, instance, "battery_level");

        deviceStatus["battery"] = "OK";
      }
    }

    //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" && SETTINGS.maintenance_mode) {
        sendNotification("switchLogic", rvoTbName, "door_opened", {}, "", SEND_TO.tb, instance, "rvo_door");
      }

      if (value === "open" && !SETTINGS.maintenance_mode) {
        sendNotification("switchLogic", rvoTbName, "door_opened_without_permission", {}, "", SEND_TO.tb, instance, "rvo_door");

        // 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") turnAlarm("on");
      }

      if (value === "closed") {
        if (alarmStatus == "ON") turnAlarm("off");
        sendNotification("switchLogic", rvoTbName, "door_closed", {}, "", SEND_TO.tb, instance, "rvo_door");
      }

      deviceStatus[type] = value;
    }

    //lux sensor
    else if (type == "twilight_sensor") {
      //! TODO - to show nok status, if lux value is not changing more then 10 times. 
      //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();

            newPinValue = 0;
          }
          else if (set.size !== 1 && twilightError) {
            //sendNotification("switchLogic", rvoTbName, ERRWEIGHT.NOTICE, "Lux sensor is working again", "", SEND_TO.tb, instance );
            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;
          instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "lux_sensor", value: average });

          twilight_sensor = [];

          //console.log("lux_sensor send", average);
        }
        //else console.log("lux_sensor", value, diff);
      }
    }

    else if (type == "state_of_contactor") {
      if (!(deviceStatus["state_of_contactor"][line] == value)) {
        sendNotification("switchLogic", rvoTbName, "state_of_contactor_for_line", { line: line, value: value }, "", SEND_TO.tb, instance);
      }

      deviceStatus["state_of_contactor"][line] = value;

      //true, false
      if (value === "On") value = true;
      else if (value === "Off") value = false;

      //TODO do we need to modify relays table with contactor value, if we do not use it on startup ??
      let dataChanged = false;
      if (relaysData[line].contactor !== newPinValue) {
        dataChanged = true;
        relaysData[line].contactor = newPinValue;
      }

      instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "reload_relays", line: line, value: value, dataChanged: dataChanged });
      reportLineStatus(line);

      //modify table relays
      // dbRelays.modify({ contactor: newPinValue }).where("line", line).make(function(builder) {
      //   builder.callback(function(err, response) {
      //     if(!err)
      //     {
      //       let time = 0;
      //       if(value) time = 1000 * 10;//10 sekund

      //       let dataChanged = false;
      //       if(relaysData[line].contactor != newPinValue) dataChanged = true;
      //       relaysData[line].contactor = newPinValue; // 0,1

      //       //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);

      //       reportLineStatus(line);
      //     }
      //     else 
      //     {
      //       errLogger.error("modify table relays failed", err);
      //     }

      //   });
      // });
    }

    else if (type === "state_of_breaker") {

      let valueChanged = false;
      if (newPinValue != previousValues[pinIndex]) valueChanged = true;

      if (valueChanged) {
        instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line });

        //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;

            instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "state_of_breaker", value: value, line: lineOnSameBraker[i] });

            deviceStatus["state_of_breaker"][lineOnSameBraker[i]] = value;
            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)) {
            instance.send(SEND_TO.cmd_manager, { sender: "dido_controller", cmd: "state_of_breaker", value: value, line: line + 3 });

            deviceStatus["state_of_breaker"][line + 3] = value;
            reportLineStatus(line + 3);

            values[type] = value;
            const tbname = relaysData[lineOnSameBraker].tbname;
            sendTelemetry(values, tbname);

            delete values[type];
          }
        }

      }

      if (value == "Off") values["status"] = "NOK";
      deviceStatus["state_of_breaker"][line] = value;

      reportLineStatus(line);
    }
    else return;

    values[type] = value;

    if (type == "rotary_switch_state") {
      if (SETTINGS.maintenance_mode) value = "maintenance";
      value = value.toLowerCase();
      values["power_mode"] = value;
    }

    if (newPinValue != previousValues[pinIndex]) previousValues[pinIndex] = newPinValue;
    if (Object.keys(values).length > 0 && tbname) sendTelemetry(values, tbname);
  }


  function sendTelemetry(values, tbname, date = Date.now()) {
    let dataToTb = {
      [tbname]: [
        {
          "ts": date,
          "values": values
        }
      ]
    };

    tbHandler.sendToTb(dataToTb, instance);
  }


  function isObject(item) {
    return (typeof item === "object" && !Array.isArray(item) && item !== null);
  }

}  //end of instance




//! 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
// {
//   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 }
// }

//! 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": ""
//         }
//       }
//     }
//   ]
// }
