josefkyrian

nodered 2 - fve

May 31st, 2022 (edited)
141
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JSON 187.58 KB | None | 0 0
  1. [
  2.     {
  3.         "id": "9c5e9d61.1f5fe",
  4.         "type": "subflow",
  5.         "name": "database stats",
  6.         "info": "",
  7.         "category": "",
  8.         "in": [
  9.             {
  10.                 "x": 540,
  11.                 "y": 220,
  12.                 "wires": [
  13.                     {
  14.                         "id": "cff83da3.a7ea1"
  15.                     }
  16.                 ]
  17.             }
  18.         ],
  19.         "out": [
  20.             {
  21.                 "x": 1214.0000171661377,
  22.                 "y": 226.00000190734863,
  23.                 "wires": [
  24.                     {
  25.                         "id": "5988a52b493c9716",
  26.                         "port": 0
  27.                     }
  28.                 ]
  29.             }
  30.         ]
  31.     },
  32.     {
  33.         "id": "cff83da3.a7ea1",
  34.         "type": "function",
  35.         "z": "9c5e9d61.1f5fe",
  36.         "name": "",
  37.         "func": "if (!msg.stats.where) {\n    msg.stats.where = '1 == 1';\n}\n\nlet sql = `\n    SELECT timestamp, ${msg.stats.agregateFunction}(value) as value FROM (\n        SELECT\n            UNIX_TIMESTAMP(DATE_FORMAT(${msg.stats.column_date}, '%Y-%m-%d-%H:%i:00')) * 1000 as \\`timestamp\\`,\n            ${msg.stats.column_value} as value\n            FROM\n                ${msg.stats.table}\n            WHERE\n                ${msg.stats.column_date} > NOW() - INTERVAL ${msg.stats.interval}\n                AND ${msg.stats.where}\n    ) ss\n    GROUP BY\n        \\`timestamp\\`\n`;\nmsg.payload = []\nmsg.topic = sql;\n//node.warn(sql);\nreturn msg;\n",
  38.         "outputs": 1,
  39.         "noerr": 0,
  40.         "initialize": "",
  41.         "finalize": "",
  42.         "libs": [],
  43.         "x": 670,
  44.         "y": 220,
  45.         "wires": [
  46.             [
  47.                 "6e210fc3.ed7a1"
  48.             ]
  49.         ]
  50.     },
  51.     {
  52.         "id": "6e210fc3.ed7a1",
  53.         "type": "mysql",
  54.         "z": "9c5e9d61.1f5fe",
  55.         "mydb": "76a2023f.8c254c",
  56.         "name": "",
  57.         "x": 830.9999771118164,
  58.         "y": 219,
  59.         "wires": [
  60.             [
  61.                 "5988a52b493c9716"
  62.             ]
  63.         ]
  64.     },
  65.     {
  66.         "id": "a9df1a10.6311e8",
  67.         "type": "function",
  68.         "z": "9c5e9d61.1f5fe",
  69.         "d": true,
  70.         "name": "dashboard v2.x",
  71.         "func": "let data = [];\n\nmsg.payload.forEach((v, k) => {\n    data.push([\n        v.timestamp,\n        v.value\n    ]);\n});\n\nmsg.payload = [\n    {\n        \"key\": `${msg.stats.column_value}`,\n        \"values\": data\n    }\n]\n\nreturn msg;",
  72.         "outputs": 1,
  73.         "noerr": 0,
  74.         "initialize": "",
  75.         "finalize": "",
  76.         "libs": [],
  77.         "x": 1054.000114440918,
  78.         "y": 284.00000381469727,
  79.         "wires": [
  80.             []
  81.         ]
  82.     },
  83.     {
  84.         "id": "5988a52b493c9716",
  85.         "type": "function",
  86.         "z": "9c5e9d61.1f5fe",
  87.         "name": "dashboard v3.x",
  88.         "func": "let data = [];\n\nmsg.payload.forEach((v, k) => {\n    data.push({\n        x:v.timestamp,\n        y:v.value\n    });\n});\n\nmsg.payload = [\n    {\n        \"series\": [\"value\"],\n        \"data\": [data],\n        \"labels\": [\"\"]\n    }\n]\n\nreturn msg;",
  89.         "outputs": 1,
  90.         "noerr": 0,
  91.         "initialize": "",
  92.         "finalize": "",
  93.         "libs": [],
  94.         "x": 1049.8000144958496,
  95.         "y": 225.00000381469727,
  96.         "wires": [
  97.             []
  98.         ]
  99.     },
  100.     {
  101.         "id": "303c9862.ddfff8",
  102.         "type": "tab",
  103.         "label": "FVE",
  104.         "disabled": false,
  105.         "info": ""
  106.     },
  107.     {
  108.         "id": "f0ca0f93933a6266",
  109.         "type": "junction",
  110.         "z": "303c9862.ddfff8",
  111.         "x": 549.8600784540176,
  112.         "y": 657.4985766410828,
  113.         "wires": [
  114.             [
  115.                 "5d07132555535339",
  116.                 "0f816f3ad14dd816",
  117.                 "0d35bbeb61b3425d"
  118.             ]
  119.         ]
  120.     },
  121.     {
  122.         "id": "2c54ae5b8ba85505",
  123.         "type": "junction",
  124.         "z": "303c9862.ddfff8",
  125.         "x": 1522.2407758235931,
  126.         "y": 3605.2983433380723,
  127.         "wires": [
  128.             [
  129.                 "8e3f2accc4d47cf1",
  130.                 "d0c51b4e08b32eb2",
  131.                 "535ea969ba5e5c16",
  132.                 "5f18183f56309951",
  133.                 "dd5dd723ead7dd78",
  134.                 "8d0de0a23b20e624",
  135.                 "656b1803a57a159b",
  136.                 "b562397f4dc61e83",
  137.                 "7ba79850621f4c7b"
  138.             ]
  139.         ]
  140.     },
  141.     {
  142.         "id": "c4597790.33e678",
  143.         "type": "function",
  144.         "z": "303c9862.ddfff8",
  145.         "name": "",
  146.         "func": "for (const record of msg.payload.forecasts) {\n    const endDate = record.period_end.substr(0, 19).replace('T', ' ');\n    msg.payload = [\n        endDate,\n        endDate,\n        msg.rooftopId,\n        record.pv_estimate,\n        record.pv_estimate10,\n        record.pv_estimate90,\n    ];\n    msg.topic = `INSERT INTO pv_forecast SET\n        request_date = NOW(),\n        forecast_from_date = CONVERT_TZ(?, '+00:00', 'SYSTEM') - INTERVAL 30 MINUTE,\n        forecast_to_date = CONVERT_TZ(?, '+00:00', 'SYSTEM'),\n        pv_rooftop_id = ?,\n        kilowatts = ?,\n        kilowatts_min = ?,\n        kilowatts_max = ?\n    `;\n    node.send(msg);\n}\n",
  147.         "outputs": 1,
  148.         "noerr": 0,
  149.         "initialize": "",
  150.         "finalize": "",
  151.         "libs": [],
  152.         "x": 1061.0001182556152,
  153.         "y": 368.00000381469727,
  154.         "wires": [
  155.             [
  156.                 "d069bd08.9ae74",
  157.                 "12df0984d0ecf73c"
  158.             ]
  159.         ]
  160.     },
  161.     {
  162.         "id": "d069bd08.9ae74",
  163.         "type": "mysql",
  164.         "z": "303c9862.ddfff8",
  165.         "mydb": "76a2023f.8c254c",
  166.         "name": "",
  167.         "x": 1269.0001220703125,
  168.         "y": 368.00000381469727,
  169.         "wires": [
  170.             []
  171.         ]
  172.     },
  173.     {
  174.         "id": "e2033b47.3c6a98",
  175.         "type": "comment",
  176.         "z": "303c9862.ddfff8",
  177.         "name": "solcast DB",
  178.         "info": "",
  179.         "x": 327,
  180.         "y": 307.99997091293335,
  181.         "wires": []
  182.     },
  183.     {
  184.         "id": "54dfb091.cc0db",
  185.         "type": "http in",
  186.         "z": "303c9862.ddfff8",
  187.         "name": "",
  188.         "url": "/goodwe-pv-inverter-record",
  189.         "method": "post",
  190.         "upload": false,
  191.         "swaggerDoc": "",
  192.         "x": 463.8952331542969,
  193.         "y": 1345.3619434833527,
  194.         "wires": [
  195.             [
  196.                 "d2bc7cbb.1c069",
  197.                 "962e1edb.38107",
  198.                 "dd943cb8.da362",
  199.                 "c3f6137434653aca"
  200.             ]
  201.         ]
  202.     },
  203.     {
  204.         "id": "d2bc7cbb.1c069",
  205.         "type": "debug",
  206.         "z": "303c9862.ddfff8",
  207.         "name": "",
  208.         "active": false,
  209.         "tosidebar": true,
  210.         "console": false,
  211.         "tostatus": false,
  212.         "complete": "true",
  213.         "targetType": "full",
  214.         "statusVal": "",
  215.         "statusType": "auto",
  216.         "x": 719.0952606201172,
  217.         "y": 1292.7619869709015,
  218.         "wires": []
  219.     },
  220.     {
  221.         "id": "b40fcb52.2ad378",
  222.         "type": "http response",
  223.         "z": "303c9862.ddfff8",
  224.         "name": "",
  225.         "statusCode": "",
  226.         "headers": {},
  227.         "x": 898.0952587127686,
  228.         "y": 1365.7619888782501,
  229.         "wires": []
  230.     },
  231.     {
  232.         "id": "962e1edb.38107",
  233.         "type": "function",
  234.         "z": "303c9862.ddfff8",
  235.         "name": "",
  236.         "func": "msg.payload = {status: 200}\nreturn msg;",
  237.         "outputs": 1,
  238.         "noerr": 0,
  239.         "initialize": "",
  240.         "finalize": "",
  241.         "libs": [
  242.             {
  243.                 "var": "fs",
  244.                 "module": "fs"
  245.             }
  246.         ],
  247.         "x": 723.0952529907227,
  248.         "y": 1357.7619888782501,
  249.         "wires": [
  250.             [
  251.                 "b40fcb52.2ad378"
  252.             ]
  253.         ]
  254.     },
  255.     {
  256.         "id": "38713333.c0669c",
  257.         "type": "comment",
  258.         "z": "303c9862.ddfff8",
  259.         "name": "logger",
  260.         "info": "",
  261.         "x": 242.09523010253906,
  262.         "y": 1261.3619244098663,
  263.         "wires": []
  264.     },
  265.     {
  266.         "id": "dd943cb8.da362",
  267.         "type": "function",
  268.         "z": "303c9862.ddfff8",
  269.         "name": "",
  270.         "func": "const columns = Object.keys(msg.payload).map((column) => `${column} = ?`).join(', ');\n\nmsg.topic = `INSERT INTO pv_inverter_record SET log_date = NOW(), ${columns}`;\nmsg.payload = Object.values(msg.payload);\n\nreturn msg;\n",
  271.         "outputs": 1,
  272.         "noerr": 0,
  273.         "initialize": "",
  274.         "finalize": "",
  275.         "libs": [],
  276.         "x": 860.0952301025391,
  277.         "y": 1449.3619244098663,
  278.         "wires": [
  279.             [
  280.                 "3adb4230412c1917"
  281.             ]
  282.         ]
  283.     },
  284.     {
  285.         "id": "94c83d02.02f7c",
  286.         "type": "debug",
  287.         "z": "303c9862.ddfff8",
  288.         "name": "",
  289.         "active": false,
  290.         "tosidebar": true,
  291.         "console": false,
  292.         "tostatus": false,
  293.         "complete": "false",
  294.         "statusVal": "",
  295.         "statusType": "auto",
  296.         "x": 1280.095230102539,
  297.         "y": 1418.3619244098663,
  298.         "wires": []
  299.     },
  300.     {
  301.         "id": "1f2bf028.4ec1c",
  302.         "type": "ui_text",
  303.         "z": "303c9862.ddfff8",
  304.         "group": "312b72db.d7629e",
  305.         "order": 2,
  306.         "width": 0,
  307.         "height": 0,
  308.         "name": "",
  309.         "label": "House Consumption",
  310.         "format": "{{msg.payload.ac_house_consumption_maxof}}W ({{msg.payload.ac_house_consumption}}W)",
  311.         "layout": "row-spread",
  312.         "x": 1379.0952320098877,
  313.         "y": 1610.5620663166046,
  314.         "wires": []
  315.     },
  316.     {
  317.         "id": "efdc183d.d80958",
  318.         "type": "function",
  319.         "z": "303c9862.ddfff8",
  320.         "name": "",
  321.         "func": "/*node.warn({\n    ac_load_p1: msg.payload.ac_load_p1,\n    ac_pgrid1: msg.payload.ac_pgrid1,\n    backup_p1: msg.payload.backup_p1,\n});*/\n\nmsg.payload.lastUpdateTime = dateformat(new Date(), \"yyyy-mm-dd HH:MM:ss\");\n\n// Pokud je to ostrovni rezim, tak hodnoty pro ac_load chodi stejny jako pro backup\n// Ale v normalnim rezimu On-Grid, je to rozdelene - na backup chodi jen to co je na backupu a na ac_load jen to co je v normalni siti\n// Takze v ostrovnim rezimu se chceme tvarit jako ze ac_load je nulovy\nif (msg.payload.work_mode_label == \"Normal (Off-Grid)\") {\n    msg.payload.ac_load_p1 = 0;\n    msg.payload.ac_load_p2 = 0;\n    msg.payload.ac_load_p3 = 0;\n}\n\n// spotreba domu vzit jako max z reportovane spotreby domu a souctu vystupu na backupu a vystupu do site\nmsg.payload.ac_house_consumption_maxof = Math.max(\n    msg.payload.ac_house_consumption,\n    (msg.payload.ac_load_p1+msg.payload.ac_load_p2+msg.payload.ac_load_p3) + (msg.payload.backup_p1+msg.payload.backup_p2+msg.payload.backup_p3)\n);\n\n// spocitat volne dostupnou energii\n// ta je jako soucet toho co se exportuje do gridu a do baterie\nconst batteryRecord = flow.get('batteryRecord');\nmsg.payload.freeAvailableEnergy = \n    msg.payload.grid_active_power\n    +\n    batteryRecord.batteryPower\n;\n\nmsg.payload.pv_ppv_percent1 = Math.round(100 * (msg.payload.pv_ppv1/1000) / flow.get('pv_rooftop_power1'));\nmsg.payload.pv_ppv_percent2 = Math.round(100 * (msg.payload.pv_ppv2 / 1000) / flow.get('pv_rooftop_power2'));\n\nflow.set('pvInverterRecord', msg.payload);\n\nreturn msg;",
  322.         "outputs": 1,
  323.         "noerr": 0,
  324.         "initialize": "",
  325.         "finalize": "",
  326.         "libs": [
  327.             {
  328.                 "var": "dateformat",
  329.                 "module": "dateformat"
  330.             }
  331.         ],
  332.         "x": 963.0953235626221,
  333.         "y": 1706.562043428421,
  334.         "wires": [
  335.             [
  336.                 "8f66dcf7.548df",
  337.                 "54170f47.5f04d",
  338.                 "86b20f44.38068",
  339.                 "8485efb6.66136",
  340.                 "3163b2fe.f1ff5e",
  341.                 "75307869.913298",
  342.                 "74496f57.36c2a",
  343.                 "118f534d.d832bd",
  344.                 "d2264ec6.39eaa",
  345.                 "203e54fa.eef5ac",
  346.                 "ab490cf4.8d57c",
  347.                 "811c3f65.b4f79",
  348.                 "1f2bf028.4ec1c",
  349.                 "7a1b5a43ecb17018",
  350.                 "24aa274ef9f69f21",
  351.                 "1c6d03dba19e8225",
  352.                 "e0c76b9c8b0a812d",
  353.                 "5b12492d69e13db2",
  354.                 "dc5ecd1673d5073d",
  355.                 "c94a357adb066e7e"
  356.             ]
  357.         ]
  358.     },
  359.     {
  360.         "id": "8f66dcf7.548df",
  361.         "type": "ui_text",
  362.         "z": "303c9862.ddfff8",
  363.         "group": "312b72db.d7629e",
  364.         "order": 1,
  365.         "width": 0,
  366.         "height": 0,
  367.         "name": "",
  368.         "label": "PV",
  369.         "format": "{{msg.payload.pv_ppv}}W ({{msg.payload.pv_ppv1}}+{{msg.payload.pv_ppv2}})",
  370.         "layout": "row-spread",
  371.         "className": "",
  372.         "x": 1326.0952529907227,
  373.         "y": 1657.5620243549347,
  374.         "wires": []
  375.     },
  376.     {
  377.         "id": "54170f47.5f04d",
  378.         "type": "ui_text",
  379.         "z": "303c9862.ddfff8",
  380.         "d": true,
  381.         "group": "312b72db.d7629e",
  382.         "order": 3,
  383.         "width": 0,
  384.         "height": 0,
  385.         "name": "",
  386.         "label": "Battery",
  387.         "format": "{{msg.payload.battery_pbattery1*-1}}W",
  388.         "layout": "row-spread",
  389.         "x": 1338.095230102539,
  390.         "y": 1699.5619976520538,
  391.         "wires": []
  392.     },
  393.     {
  394.         "id": "83d4257e.51d728",
  395.         "type": "ui_text",
  396.         "z": "303c9862.ddfff8",
  397.         "group": "312b72db.d7629e",
  398.         "order": 5,
  399.         "width": 0,
  400.         "height": 0,
  401.         "name": "Grid",
  402.         "label": "Grid ({{msg.payload.grid_direction}})",
  403.         "format": "{{msg.payload.grid_active_power}}W ({{msg.payload.grid_meter_active_power1 >= 0 ? \"+\"+msg.payload.grid_meter_active_power1 : msg.payload.grid_meter_active_power1}}{{msg.payload.grid_meter_active_power2 >= 0 ? \"+\"+msg.payload.grid_meter_active_power2 : msg.payload.grid_meter_active_power2}}{{msg.payload.grid_meter_active_power3 >= 0 ? \"+\"+msg.payload.grid_meter_active_power3 : msg.payload.grid_meter_active_power3}})",
  404.         "layout": "row-spread",
  405.         "x": 1387.095266342163,
  406.         "y": 1751.5619661808014,
  407.         "wires": []
  408.     },
  409.     {
  410.         "id": "86b20f44.38068",
  411.         "type": "ui_text",
  412.         "z": "303c9862.ddfff8",
  413.         "group": "b55f633.0a700a",
  414.         "order": 0,
  415.         "width": 0,
  416.         "height": 0,
  417.         "name": "",
  418.         "label": "SOC",
  419.         "format": "{{msg.payload.battery_soc}}%",
  420.         "layout": "row-spread",
  421.         "x": 1327.095230102539,
  422.         "y": 1844.1619732379913,
  423.         "wires": []
  424.     },
  425.     {
  426.         "id": "8485efb6.66136",
  427.         "type": "ui_text",
  428.         "z": "303c9862.ddfff8",
  429.         "group": "b55f633.0a700a",
  430.         "order": 0,
  431.         "width": 0,
  432.         "height": 0,
  433.         "name": "",
  434.         "label": "Mode",
  435.         "format": "{{msg.payload.battery_mode_label}}",
  436.         "layout": "row-spread",
  437.         "x": 1330.0952281951904,
  438.         "y": 1879.1620209217072,
  439.         "wires": []
  440.     },
  441.     {
  442.         "id": "4d42466c.cea3e8",
  443.         "type": "inject",
  444.         "z": "303c9862.ddfff8",
  445.         "name": "",
  446.         "props": [
  447.             {
  448.                 "p": "payload"
  449.             },
  450.             {
  451.                 "p": "topic",
  452.                 "vt": "str"
  453.             }
  454.         ],
  455.         "repeat": "600",
  456.         "crontab": "",
  457.         "once": true,
  458.         "onceDelay": 0.1,
  459.         "topic": "",
  460.         "payload": "",
  461.         "payloadType": "date",
  462.         "x": 478.6667289733887,
  463.         "y": 860.3333446979523,
  464.         "wires": [
  465.             [
  466.                 "bbd40e52.e5e28"
  467.             ]
  468.         ]
  469.     },
  470.     {
  471.         "id": "bbd40e52.e5e28",
  472.         "type": "function",
  473.         "z": "303c9862.ddfff8",
  474.         "name": "",
  475.         "func": "const af = global.get('actionflows');\n\nconst resMsg = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: \"SELECT * FROM pv_rooftop\",\n    values: [],\n});\n\nlet totalPower = 0;\n\nfor (const rooftop of resMsg.payload) {\n    const resMsg2 = await af.invoke('core.db.query', {\n        database: 'smarthouse',\n        query: `\n            SELECT * FROM pv_forecast\n        \tWHERE pv_rooftop_id = ? AND forecast_from_date >= NOW()\n        \tORDER by forecast_from_date ASC, request_date DESC LIMIT 1\n    \t`,\n        values: [rooftop.id],\n    });\n    \n    if (resMsg2.payload.length) {\n        totalPower += resMsg2.payload[0].kilowatts * rooftop.power;\n    }\n}\n\nmsg.payload = Math.round(totalPower * 1000);\nreturn msg;",
  476.         "outputs": 1,
  477.         "noerr": 0,
  478.         "initialize": "",
  479.         "finalize": "",
  480.         "libs": [],
  481.         "x": 660.6667289733887,
  482.         "y": 865.3333446979523,
  483.         "wires": [
  484.             [
  485.                 "194df1a8.097f6e",
  486.                 "3a31dfb9.55bf9"
  487.             ]
  488.         ]
  489.     },
  490.     {
  491.         "id": "3a31dfb9.55bf9",
  492.         "type": "debug",
  493.         "z": "303c9862.ddfff8",
  494.         "name": "",
  495.         "active": false,
  496.         "tosidebar": true,
  497.         "console": false,
  498.         "tostatus": false,
  499.         "complete": "false",
  500.         "statusVal": "",
  501.         "statusType": "auto",
  502.         "x": 892.666748046875,
  503.         "y": 851.9333031177521,
  504.         "wires": []
  505.     },
  506.     {
  507.         "id": "194df1a8.097f6e",
  508.         "type": "ui_text",
  509.         "z": "303c9862.ddfff8",
  510.         "group": "d9bbfe4e.3d039",
  511.         "order": 1,
  512.         "width": 0,
  513.         "height": 0,
  514.         "name": "",
  515.         "label": "Now",
  516.         "format": "{{msg.payload}}W",
  517.         "layout": "row-spread",
  518.         "className": "",
  519.         "x": 881.666748046875,
  520.         "y": 902.333377122879,
  521.         "wires": []
  522.     },
  523.     {
  524.         "id": "e136e96b.2dbfe8",
  525.         "type": "function",
  526.         "z": "303c9862.ddfff8",
  527.         "name": "",
  528.         "func": "//msg.topic = `select UNIX_TIMESTAMP(forecast_from_date) as forecast_from_date, UNIX_TIMESTAMP(forecast_to_date) as forecast_to_date, roof_name, kilowatts from pv_solcast_forecast where request_date = (SELECT request_date FROM pv_solcast_forecast WHERE forecast_from_date >= NOW() ORDER BY forecast_from_date, request_date DESC LIMIT 1) AND forecast_from_date >= NOW() AND forecast_to_date < DATE(NOW())+interval 1 day order by forecast_from_date`;\nmsg.topic = `\n    WITH\n    \tpv_forecast_ranked_1 AS (\n    \t\tselect\n    \t\t\tpv_forecast.forecast_to_date, pv_forecast.kilowatts*pv_rooftop.power AS rooftop_kilowatts, ROW_NUMBER() OVER (PARTITION BY forecast_from_date ORDER BY request_date DESC) AS rn\n    \t\tfrom\n    \t\t\tpv_forecast\n    \t\tJOIN pv_rooftop ON pv_rooftop.id = pv_rooftop_id\n    \t\twhere\n    \t\t\tDATE(NOW() + INTERVAL ${msg.todayOffset} DAY) = DATE(forecast_to_date)\n    \t\t\tand pv_rooftop_id = 1\n    \t\torder by forecast_to_date, request_date\n    \t),\n    \tpv_forecast_ranked_2 AS (\n    \t\tselect\n    \t\t\tpv_forecast.forecast_to_date, pv_forecast.kilowatts*pv_rooftop.power AS rooftop_kilowatts, ROW_NUMBER() OVER (PARTITION BY forecast_from_date ORDER BY request_date DESC) AS rn\n    \t\tfrom\n    \t\t\tpv_forecast\n\t\t\tJOIN pv_rooftop ON pv_rooftop.id = pv_rooftop_id\n    \t\twhere\n    \t\t\tDATE(NOW() + INTERVAL ${msg.todayOffset} DAY) = DATE(forecast_to_date)\n    \t\t\tand pv_rooftop_id = 2\n    \t\torder by forecast_to_date, request_date\n    \t)\n    \n    SELECT * FROM (\n    \tSELECT time, total_power1 AS forecast_rest_power1, total_power2 AS forecast_rest_power2 FROM (\n    \t\tSELECT time, @curr1:=(string1)*(ABS(IF(@prevTime IS NULL, 0, time-@prevTime))/3600) as kwh1, @curr2:=(string2)*(ABS(IF(@prevTime IS NULL, 0, time-@prevTime))/3600) as kwh2, @e1:=@e1+@curr1 AS total_power1, @e2:=@e2+@curr2 AS total_power2, @prevTime:=time FROM (\n    \t\t\tSELECT\n    \t\t\tUNIX_TIMESTAMP(pv_forecast_ranked_1.forecast_to_date) AS \"time\",\n    \t\t\tpv_forecast_ranked_1.rooftop_kilowatts AS string1, IFNULL(pv_forecast_ranked_2.rooftop_kilowatts, 0) AS string2\n    \t\t\tFROM\n    \t\t\tpv_forecast_ranked_1\n\t\t\t\tLEFT JOIN pv_forecast_ranked_2 ON pv_forecast_ranked_1.forecast_to_date = pv_forecast_ranked_2.forecast_to_date AND pv_forecast_ranked_2.rn = 1\n    \t\t\tWHERE\n    \t\t\tpv_forecast_ranked_1.rn = 1\n    \t\t\tORDER BY time DESC\n    \t\t) forecast, (SELECT@e1:=0)e1, (SELECT@e2:=0)e2, (SELECT@prevTime:=NULL)prevTime\n    \t) forecast2\n    \tORDER BY time ASC\n    ) forecast3\n`;\nmsg.payload = [];\n\nreturn msg;\n",
  529.         "outputs": 1,
  530.         "noerr": 0,
  531.         "initialize": "",
  532.         "finalize": "",
  533.         "libs": [],
  534.         "x": 636.66672706604,
  535.         "y": 1044.1333572864532,
  536.         "wires": [
  537.             [
  538.                 "67449735.a106e8"
  539.             ]
  540.         ]
  541.     },
  542.     {
  543.         "id": "67449735.a106e8",
  544.         "type": "mysql",
  545.         "z": "303c9862.ddfff8",
  546.         "mydb": "76a2023f.8c254c",
  547.         "name": "",
  548.         "x": 807.6667461395264,
  549.         "y": 1039.13338971138,
  550.         "wires": [
  551.             [
  552.                 "f59eeb5f.e41d88",
  553.                 "c97e520.1152db"
  554.             ]
  555.         ]
  556.     },
  557.     {
  558.         "id": "adf8461f.01e548",
  559.         "type": "ui_text",
  560.         "z": "303c9862.ddfff8",
  561.         "group": "d9bbfe4e.3d039",
  562.         "order": 3,
  563.         "width": 0,
  564.         "height": 0,
  565.         "name": "",
  566.         "label": "Power Today",
  567.         "format": "{{msg.payload.total}}kWh ({{msg.payload.total1}}+{{msg.payload.total2}})",
  568.         "layout": "row-spread",
  569.         "className": "",
  570.         "x": 1178.6668167114258,
  571.         "y": 1075.1334142684937,
  572.         "wires": []
  573.     },
  574.     {
  575.         "id": "a0f2c0a1.2d959",
  576.         "type": "inject",
  577.         "z": "303c9862.ddfff8",
  578.         "name": "",
  579.         "props": [
  580.             {
  581.                 "p": "payload"
  582.             },
  583.             {
  584.                 "p": "todayOffset",
  585.                 "v": "0",
  586.                 "vt": "str"
  587.             }
  588.         ],
  589.         "repeat": "600",
  590.         "crontab": "",
  591.         "once": true,
  592.         "onceDelay": 0.1,
  593.         "topic": "",
  594.         "payload": "",
  595.         "payloadType": "date",
  596.         "x": 483.66673469543457,
  597.         "y": 1027.1333782672882,
  598.         "wires": [
  599.             [
  600.                 "e136e96b.2dbfe8"
  601.             ]
  602.         ]
  603.     },
  604.     {
  605.         "id": "f59eeb5f.e41d88",
  606.         "type": "debug",
  607.         "z": "303c9862.ddfff8",
  608.         "name": "",
  609.         "active": false,
  610.         "tosidebar": true,
  611.         "console": false,
  612.         "tostatus": false,
  613.         "complete": "false",
  614.         "statusVal": "",
  615.         "statusType": "auto",
  616.         "x": 978.6667442321777,
  617.         "y": 992.1333782672882,
  618.         "wires": []
  619.     },
  620.     {
  621.         "id": "bb805d7.6002ea",
  622.         "type": "ui_gauge",
  623.         "z": "303c9862.ddfff8",
  624.         "name": "Battery SOC",
  625.         "group": "819686be.5e6a58",
  626.         "order": 6,
  627.         "width": 0,
  628.         "height": 0,
  629.         "gtype": "wave",
  630.         "title": "Battery SOC ({{msg.batteryChangeSpeedText}})",
  631.         "label": "%",
  632.         "format": "{{value}}",
  633.         "min": 0,
  634.         "max": "100",
  635.         "colors": [
  636.             "#00b500",
  637.             "#e6e600",
  638.             "#ca3838"
  639.         ],
  640.         "seg1": "",
  641.         "seg2": "",
  642.         "x": 1392.0952644348145,
  643.         "y": 2367.561976671219,
  644.         "wires": []
  645.     },
  646.     {
  647.         "id": "3163b2fe.f1ff5e",
  648.         "type": "function",
  649.         "z": "303c9862.ddfff8",
  650.         "name": "",
  651.         "func": "msg.batteryChangeSpeedText = flow.get('batteryRecord').batteryChangeSpeedText;\nmsg.payload = msg.payload.battery_soc;\nreturn msg;",
  652.         "outputs": 1,
  653.         "noerr": 0,
  654.         "initialize": "",
  655.         "finalize": "",
  656.         "libs": [],
  657.         "x": 1231.0952682495117,
  658.         "y": 2364.5619747638702,
  659.         "wires": [
  660.             [
  661.                 "bb805d7.6002ea"
  662.             ]
  663.         ]
  664.     },
  665.     {
  666.         "id": "75307869.913298",
  667.         "type": "function",
  668.         "z": "303c9862.ddfff8",
  669.         "name": "",
  670.         "func": "msg.payload = msg.payload.pv_ppv;\nreturn msg;",
  671.         "outputs": 1,
  672.         "noerr": 0,
  673.         "initialize": "",
  674.         "finalize": "",
  675.         "x": 1234.0952682495117,
  676.         "y": 2252.5620715618134,
  677.         "wires": [
  678.             [
  679.                 "9b497417b6425c53"
  680.             ]
  681.         ]
  682.     },
  683.     {
  684.         "id": "bb2004f2.959448",
  685.         "type": "ui_gauge",
  686.         "z": "303c9862.ddfff8",
  687.         "d": true,
  688.         "name": "Battery",
  689.         "group": "819686be.5e6a58",
  690.         "order": 3,
  691.         "width": 0,
  692.         "height": 0,
  693.         "gtype": "gage",
  694.         "title": "Battery - {{msg.batteryMode}}",
  695.         "label": "W",
  696.         "format": "{{value}}",
  697.         "min": "-8000",
  698.         "max": "8000",
  699.         "colors": [
  700.             "#b30000",
  701.             "#ffffff",
  702.             "#36cc00"
  703.         ],
  704.         "seg1": "",
  705.         "seg2": "",
  706.         "x": 1394.0952682495117,
  707.         "y": 2316.561976671219,
  708.         "wires": []
  709.     },
  710.     {
  711.         "id": "74496f57.36c2a",
  712.         "type": "function",
  713.         "z": "303c9862.ddfff8",
  714.         "name": "",
  715.         "func": "let batteryPower = msg.payload.battery_pbattery1*-1;\nlet batteryMode = msg.payload.battery_mode_label; \n\nif (batteryMode == \"Discharge\" && batteryPower >= 0) {\n    batteryPower = 0;\n    if (msg.payload.battery_soc >= 100) {\n        batteryMode = \"Fully Charged\";\n    }else {\n        batteryMode = \"Standby\";\n    }\n}\n\nmsg.batteryMode = batteryMode;\nmsg.payload = batteryPower;\nreturn msg;",
  716.         "outputs": 1,
  717.         "noerr": 0,
  718.         "initialize": "",
  719.         "finalize": "",
  720.         "libs": [],
  721.         "x": 1227.0952682495117,
  722.         "y": 2314.561976671219,
  723.         "wires": [
  724.             [
  725.                 "bb2004f2.959448"
  726.             ]
  727.         ]
  728.     },
  729.     {
  730.         "id": "3aab581b.326c98",
  731.         "type": "function",
  732.         "z": "303c9862.ddfff8",
  733.         "name": "Zatížení fází",
  734.         "func": "const phasesLoad = [\n    Math.round(100 * msg.payload.backup_p1 / 3300),\n    Math.round(100 * msg.payload.backup_p2 / 3300),\n    Math.round(100 * msg.payload.backup_p3 / 3300),\n];\n\nfunction notifyLoad(phase, currentLoad)\n{\n    const lastNotifiedTime = flow.get(`lastNotifiedBackupLoadTime_${phase}`) || 0;\n    if (Date.now() - lastNotifiedTime > 5*1000) {\n        flow.set(`lastNotifiedBackupLoadTime_${phase}`, Date.now());\n        \n        const lastNotifiedBackupLoad = flow.get(`lastNotifiedBackupLoadValue_${phase}`) || 0;\n        const diff = Math.abs(currentLoad - lastNotifiedBackupLoad);\n        \n        // notifikovat pokud je laod vetsi nez 95 popr. pokud se zvednul o dalsich 10\n        // nebo pokud byl load > 95 a uz je pod 70\n        const upperThreshold = 110;\n        const lowerThreshold = 70;\n        if ((currentLoad > upperThreshold && currentLoad > lastNotifiedBackupLoad + 10) || (lastNotifiedBackupLoad > upperThreshold && currentLoad <= lowerThreshold)) {\n            flow.set(`lastNotifiedBackupLoadValue_${phase}`, currentLoad);\n            \n            node.warn(`Load changed (phase: ${phase}, current load: ${currentLoad}, previousLoad: ${lastNotifiedBackupLoad})`);\n            msg.payload = {\"title\" : `Zatížení fáze L${phase}. ${currentLoad}%`, \"text\" : \"\", \"event\" : \"speech\", \"options\" : {\"telegram\" : false}}\n            node.send(msg);\n        }\n    }\n}\n\nnotifyLoad(1, phasesLoad[0]);\nnotifyLoad(2, phasesLoad[1]);\nnotifyLoad(3, phasesLoad[2]);\n",
  735.         "outputs": 1,
  736.         "noerr": 0,
  737.         "initialize": "",
  738.         "finalize": "",
  739.         "libs": [],
  740.         "x": 873.0953063964844,
  741.         "y": 2677.7623703479767,
  742.         "wires": [
  743.             [
  744.                 "bddab30d.0dddc"
  745.             ]
  746.         ]
  747.     },
  748.     {
  749.         "id": "bddab30d.0dddc",
  750.         "type": "actionflows",
  751.         "z": "303c9862.ddfff8",
  752.         "info": "Describe your action API here.",
  753.         "untilproptype": "num",
  754.         "proptype": "msg",
  755.         "name": "notify",
  756.         "prop": "loop",
  757.         "untilprop": 0,
  758.         "until": "gt",
  759.         "loop": "none",
  760.         "scope": "global",
  761.         "perf": false,
  762.         "seq": false,
  763.         "x": 1037.0953063964844,
  764.         "y": 2679.7623703479767,
  765.         "wires": [
  766.             []
  767.         ]
  768.     },
  769.     {
  770.         "id": "d2f463e9.b9a6",
  771.         "type": "ui_text",
  772.         "z": "303c9862.ddfff8",
  773.         "group": "d1547785.b0eb48",
  774.         "order": 0,
  775.         "width": 0,
  776.         "height": 0,
  777.         "name": "",
  778.         "label": "L1",
  779.         "format": "{{msg.payload.phasesLoad[0].ac+msg.payload.phasesLoad[0].backup}}W({{msg.payload.phasesLoad[0].ac}}+{{msg.payload.phasesLoad[0].backup}}) [{{msg.payload.phasesLoad[0].percent}}%]",
  780.         "layout": "row-spread",
  781.         "x": 1387.0952892303467,
  782.         "y": 2538.1618225574493,
  783.         "wires": []
  784.     },
  785.     {
  786.         "id": "118f534d.d832bd",
  787.         "type": "function",
  788.         "z": "303c9862.ddfff8",
  789.         "name": "Phases Load",
  790.         "func": "const phasesLoad = [\n    {percent: Math.round(100 * (msg.payload.backup_p1+msg.payload.ac_load_p1) / 3300), backup: msg.payload.backup_p1, ac: msg.payload.ac_load_p1},\n    {percent: Math.round(100 * (msg.payload.backup_p2+msg.payload.ac_load_p2) / 3300), backup: msg.payload.backup_p2, ac: msg.payload.ac_load_p2},\n    {percent: Math.round(100 * (msg.payload.backup_p3+msg.payload.ac_load_p3) / 3300), backup: msg.payload.backup_p3, ac: msg.payload.ac_load_p3},\n];\n\nmsg.payload = {\n    phasesLoad,\n};\n\nreturn msg;",
  791.         "outputs": 1,
  792.         "noerr": 0,
  793.         "initialize": "",
  794.         "finalize": "",
  795.         "libs": [],
  796.         "x": 1225.0952281951904,
  797.         "y": 2539.161930322647,
  798.         "wires": [
  799.             [
  800.                 "d2f463e9.b9a6",
  801.                 "eb45726.34fae9",
  802.                 "ead65c60.5d7af"
  803.             ]
  804.         ]
  805.     },
  806.     {
  807.         "id": "eb45726.34fae9",
  808.         "type": "ui_text",
  809.         "z": "303c9862.ddfff8",
  810.         "group": "d1547785.b0eb48",
  811.         "order": 0,
  812.         "width": 0,
  813.         "height": 0,
  814.         "name": "",
  815.         "label": "L2",
  816.         "format": "{{msg.payload.phasesLoad[1].ac+msg.payload.phasesLoad[1].backup}}W({{msg.payload.phasesLoad[1].ac}}+{{msg.payload.phasesLoad[1].backup}}) [{{msg.payload.phasesLoad[1].percent}}%]",
  817.         "layout": "row-spread",
  818.         "x": 1387.095266342163,
  819.         "y": 2576.1617834568024,
  820.         "wires": []
  821.     },
  822.     {
  823.         "id": "ead65c60.5d7af",
  824.         "type": "ui_text",
  825.         "z": "303c9862.ddfff8",
  826.         "group": "d1547785.b0eb48",
  827.         "order": 0,
  828.         "width": 0,
  829.         "height": 0,
  830.         "name": "",
  831.         "label": "L3",
  832.         "format": "{{msg.payload.phasesLoad[2].ac+msg.payload.phasesLoad[2].backup}}W({{msg.payload.phasesLoad[2].ac}}+{{msg.payload.phasesLoad[2].backup}}) [{{msg.payload.phasesLoad[2].percent}}%]",
  833.         "layout": "row-spread",
  834.         "x": 1390.095266342163,
  835.         "y": 2612.1617834568024,
  836.         "wires": []
  837.     },
  838.     {
  839.         "id": "9c95fb95.b18c28",
  840.         "type": "function",
  841.         "z": "303c9862.ddfff8",
  842.         "name": "Stav FVE",
  843.         "func": "const lastNotifiedInverterStatus = flow.get(`lastNotifiedInverterStatus`);\n\nlet currentInverterStatus = `Změna stavu fotovoltaiky ${msg.payload.work_mode_label}. Stav sítě ${msg.payload.grid_mode_label}`;\n\nif (currentInverterStatus != lastNotifiedInverterStatus) {\n    flow.set(`lastNotifiedInverterStatus`, currentInverterStatus);\n    \n    msg.payload = {\"title\" : currentInverterStatus, \"text\" : \"\", \"event\" : \"speech\"}\n    node.send(msg);\n}\n",
  844.         "outputs": 1,
  845.         "noerr": 0,
  846.         "initialize": "",
  847.         "finalize": "",
  848.         "libs": [],
  849.         "x": 858.0952453613281,
  850.         "y": 2932.3623485565186,
  851.         "wires": [
  852.             [
  853.                 "6a08531c.173c0c"
  854.             ]
  855.         ]
  856.     },
  857.     {
  858.         "id": "6a08531c.173c0c",
  859.         "type": "actionflows",
  860.         "z": "303c9862.ddfff8",
  861.         "info": "Describe your action API here.",
  862.         "untilproptype": "num",
  863.         "proptype": "msg",
  864.         "name": "notify",
  865.         "prop": "loop",
  866.         "untilprop": 0,
  867.         "until": "gt",
  868.         "loop": "none",
  869.         "scope": "global",
  870.         "perf": false,
  871.         "seq": false,
  872.         "x": 1036.0953674316406,
  873.         "y": 2930.3623485565186,
  874.         "wires": [
  875.             []
  876.         ]
  877.     },
  878.     {
  879.         "id": "4dad62b3.f306fc",
  880.         "type": "function",
  881.         "z": "303c9862.ddfff8",
  882.         "name": "SOC Baterie",
  883.         "func": "const lastNotifiedSOC = flow.get(`lastNotifiedSOC`) || 0;\n\nlet currentSOC = msg.payload.battery_soc;\n\nif (\n    currentSOC % 5 == 0 /*stav baterie musi byt delitelny 5*/\n    &&\n    (\n        (currentSOC > 50 && Math.abs(currentSOC - lastNotifiedSOC) >= 10)\n        || \n        (currentSOC <= 50 && Math.abs(currentSOC - lastNotifiedSOC) >= 5)\n    )\n) {\n    flow.set(`lastNotifiedSOC`, currentSOC);\n    \n    msg.payload = {\"title\" : currentSOC > lastNotifiedSOC ? `Baterie se dobila na ${currentSOC}%` : `Baterie se vybila na ${currentSOC}%`, \"text\" : \"\", \"event\" : \"speech\"}\n    node.send(msg);\n}\n",
  884.         "outputs": 1,
  885.         "noerr": 0,
  886.         "initialize": "",
  887.         "finalize": "",
  888.         "libs": [],
  889.         "x": 855.0952415466309,
  890.         "y": 3052.162185907364,
  891.         "wires": [
  892.             [
  893.                 "d87a2bdcc3f484b0"
  894.             ]
  895.         ]
  896.     },
  897.     {
  898.         "id": "e6a603d9.ee064",
  899.         "type": "actionflows",
  900.         "z": "303c9862.ddfff8",
  901.         "info": "Describe your action API here.",
  902.         "untilproptype": "num",
  903.         "proptype": "msg",
  904.         "name": "notify",
  905.         "prop": "loop",
  906.         "untilprop": 0,
  907.         "until": "gt",
  908.         "loop": "none",
  909.         "scope": "global",
  910.         "perf": false,
  911.         "seq": false,
  912.         "x": 1276.0953102111816,
  913.         "y": 3050.162399291992,
  914.         "wires": [
  915.             []
  916.         ]
  917.     },
  918.     {
  919.         "id": "c97e520.1152db",
  920.         "type": "function",
  921.         "z": "303c9862.ddfff8",
  922.         "name": "",
  923.         "func": "const msgs = [null, null];\n\nlet total1 = null;\nlet total2 = null;\nlet rest1 = null;\nlet rest2 = null;\n\nfor (const row of msg.payload) {\n    if (total1 === null) {\n        total1 = row.forecast_rest_power1;\n    }\n    if (total2 === null) {\n        total2 = row.forecast_rest_power2;\n    }\n    if (rest1 === null && row.time*1000 >= Date.now()) {\n        rest1 = row.forecast_rest_power1;\n        rest2 = row.forecast_rest_power2;\n    }\n}\n\nmsgs[msg.todayOffset] = {\n    payload: {\n        total: Math.round((total1 + total2) * 10) / 10,\n        total1: Math.round(total1 * 10) / 10,\n        total2: Math.round(total2 * 10) / 10,\n        rest: Math.round((rest1 + rest2) * 10) / 10,\n        rest1: Math.round(rest1 * 10) / 10,\n        rest2: Math.round(rest2 * 10) / 10,\n    },\n}\n\nreturn msgs;",
  924.         "outputs": 2,
  925.         "noerr": 0,
  926.         "initialize": "",
  927.         "finalize": "",
  928.         "libs": [],
  929.         "x": 991.6667423248291,
  930.         "y": 1090.3333303928375,
  931.         "wires": [
  932.             [
  933.                 "adf8461f.01e548",
  934.                 "bc31c91c29d3fac8"
  935.             ],
  936.             [
  937.                 "cbd97cf7.6245b"
  938.             ]
  939.         ]
  940.     },
  941.     {
  942.         "id": "cbd97cf7.6245b",
  943.         "type": "ui_text",
  944.         "z": "303c9862.ddfff8",
  945.         "group": "d9bbfe4e.3d039",
  946.         "order": 4,
  947.         "width": 0,
  948.         "height": 0,
  949.         "name": "",
  950.         "label": "Power Tomorrow",
  951.         "format": "{{msg.payload.total}}kWh ({{msg.payload.total1}}+{{msg.payload.total2}})",
  952.         "layout": "row-spread",
  953.         "className": "",
  954.         "x": 1187.6668167114258,
  955.         "y": 1119.3332195281982,
  956.         "wires": []
  957.     },
  958.     {
  959.         "id": "2bc43a60.d2e196",
  960.         "type": "inject",
  961.         "z": "303c9862.ddfff8",
  962.         "name": "",
  963.         "props": [
  964.             {
  965.                 "p": "payload"
  966.             },
  967.             {
  968.                 "p": "todayOffset",
  969.                 "v": "1",
  970.                 "vt": "str"
  971.             }
  972.         ],
  973.         "repeat": "600",
  974.         "crontab": "",
  975.         "once": true,
  976.         "onceDelay": 0.1,
  977.         "topic": "",
  978.         "payload": "",
  979.         "payloadType": "date",
  980.         "x": 482.66673851013184,
  981.         "y": 1082.3333790302277,
  982.         "wires": [
  983.             [
  984.                 "e136e96b.2dbfe8"
  985.             ]
  986.         ]
  987.     },
  988.     {
  989.         "id": "d2264ec6.39eaa",
  990.         "type": "ui_text",
  991.         "z": "303c9862.ddfff8",
  992.         "group": "e7d46503.f3f4b8",
  993.         "order": 1,
  994.         "width": 0,
  995.         "height": 0,
  996.         "name": "",
  997.         "label": "PV String 1",
  998.         "format": "{{msg.payload.pv_vpv1}}V {{msg.payload.pv_ipv1}}A ({{msg.payload.pv_ppv1}}W~{{msg.payload.pv_ppv_percent1}}%)",
  999.         "layout": "row-spread",
  1000.         "className": "",
  1001.         "x": 1348.0952072143555,
  1002.         "y": 2008.762088060379,
  1003.         "wires": []
  1004.     },
  1005.     {
  1006.         "id": "203e54fa.eef5ac",
  1007.         "type": "ui_text",
  1008.         "z": "303c9862.ddfff8",
  1009.         "group": "e7d46503.f3f4b8",
  1010.         "order": 2,
  1011.         "width": 0,
  1012.         "height": 0,
  1013.         "name": "",
  1014.         "label": "PV String 2",
  1015.         "format": "{{msg.payload.pv_vpv2}}V {{msg.payload.pv_ipv2}}A ({{msg.payload.pv_ppv2}}W~{{msg.payload.pv_ppv_percent2}}%)",
  1016.         "layout": "row-spread",
  1017.         "className": "",
  1018.         "x": 1351.0952072143555,
  1019.         "y": 2043.161990404129,
  1020.         "wires": []
  1021.     },
  1022.     {
  1023.         "id": "ab490cf4.8d57c",
  1024.         "type": "ui_text",
  1025.         "z": "303c9862.ddfff8",
  1026.         "group": "e7d46503.f3f4b8",
  1027.         "order": 3,
  1028.         "width": 0,
  1029.         "height": 0,
  1030.         "name": "",
  1031.         "label": "Battery",
  1032.         "format": "{{msg.payload.battery_vbattery1}}V {{msg.payload.battery_ibattery1*-1}}A",
  1033.         "layout": "row-spread",
  1034.         "x": 1342.0952281951904,
  1035.         "y": 2081.1620247364044,
  1036.         "wires": []
  1037.     },
  1038.     {
  1039.         "id": "811c3f65.b4f79",
  1040.         "type": "ui_text",
  1041.         "z": "303c9862.ddfff8",
  1042.         "group": "b55f633.0a700a",
  1043.         "order": 0,
  1044.         "width": 0,
  1045.         "height": 0,
  1046.         "name": "",
  1047.         "label": "Limit Charge/Discharge",
  1048.         "format": "{{msg.payload.battery_charge_limit}}A/{{msg.payload.battery_discharge_limit}}A",
  1049.         "layout": "row-spread",
  1050.         "x": 1396.095209121704,
  1051.         "y": 1913.7619888782501,
  1052.         "wires": []
  1053.     },
  1054.     {
  1055.         "id": "c14f51cd.0afad",
  1056.         "type": "comment",
  1057.         "z": "303c9862.ddfff8",
  1058.         "name": "predikce stavu baterie",
  1059.         "info": "",
  1060.         "x": 342.45230865478516,
  1061.         "y": 3139.7619268894196,
  1062.         "wires": []
  1063.     },
  1064.     {
  1065.         "id": "8452501d.15a64",
  1066.         "type": "ui_button",
  1067.         "z": "303c9862.ddfff8",
  1068.         "d": true,
  1069.         "name": "",
  1070.         "group": "d1c39829.3ee0e8",
  1071.         "order": 0,
  1072.         "width": 0,
  1073.         "height": 0,
  1074.         "passthru": false,
  1075.         "label": "Calculate",
  1076.         "tooltip": "",
  1077.         "color": "",
  1078.         "bgcolor": "",
  1079.         "icon": "",
  1080.         "payload": "",
  1081.         "payloadType": "str",
  1082.         "topic": "",
  1083.         "x": 487.9523239135742,
  1084.         "y": 3192.761799097061,
  1085.         "wires": [
  1086.             [
  1087.                 "81a374d23a95621c"
  1088.             ]
  1089.         ]
  1090.     },
  1091.     {
  1092.         "id": "7fab9dfb.9b56e4",
  1093.         "type": "inject",
  1094.         "z": "303c9862.ddfff8",
  1095.         "name": "",
  1096.         "props": [
  1097.             {
  1098.                 "p": "payload"
  1099.             },
  1100.             {
  1101.                 "p": "topic",
  1102.                 "vt": "str"
  1103.             }
  1104.         ],
  1105.         "repeat": "1800",
  1106.         "crontab": "",
  1107.         "once": true,
  1108.         "onceDelay": 0.1,
  1109.         "topic": "",
  1110.         "payload": "",
  1111.         "payloadType": "date",
  1112.         "x": 469.9522933959961,
  1113.         "y": 3245.761799097061,
  1114.         "wires": [
  1115.             [
  1116.                 "81a374d23a95621c"
  1117.             ]
  1118.         ]
  1119.     },
  1120.     {
  1121.         "id": "2b8e5315.f38d9c",
  1122.         "type": "function",
  1123.         "z": "303c9862.ddfff8",
  1124.         "name": "compute",
  1125.         "func": "const batteryCapacity = 14.21;\nconst predictionHours = 36;\n\n\n//-------------------------------------------------------------------------------------\nconst af = global.get('actionflows');\n\nconst batterySocPredictionEngine = (await af.invoke('core.npm.require', {payload: {\n    module: flow.get('battery_soc_prediction_module_path'),\n    cached: false,\n}})).payload;\n\nconst predictionDateFrom = batterySocPredictionEngine.getPredictionDateFrom('now');\n//node.warn(predictionDateFrom);\n\nconst dbConnection = {\n    async query(query, values) {\n        const resMsg = await af.invoke('core.db.query', {\n            database: 'smarthouse',\n            query,\n            values,\n        });\n        return resMsg.payload;\n    }\n};\nconst batterySOCPrediction = await batterySocPredictionEngine.computeBatterySOCPrediction(predictionDateFrom, predictionHours, batteryCapacity, {\n\tpvForecast: dbConnection,\n\tpvInverter: dbConnection,\n});\n\nmsg.payload = batterySOCPrediction;\n\nawait batterySocPredictionEngine.saveBatterySOCPrediction(batterySOCPrediction, predictionDateFrom, dbConnection);\n\nreturn msg;\n",
  1126.         "outputs": 1,
  1127.         "noerr": 0,
  1128.         "initialize": "",
  1129.         "finalize": "",
  1130.         "libs": [],
  1131.         "x": 845.9522972106934,
  1132.         "y": 3235.7618000507355,
  1133.         "wires": [
  1134.             [
  1135.                 "bf878c79.f3a24",
  1136.                 "d799d8e48807dbf8"
  1137.             ]
  1138.         ]
  1139.     },
  1140.     {
  1141.         "id": "bf878c79.f3a24",
  1142.         "type": "debug",
  1143.         "z": "303c9862.ddfff8",
  1144.         "name": "",
  1145.         "active": false,
  1146.         "tosidebar": true,
  1147.         "console": false,
  1148.         "tostatus": false,
  1149.         "complete": "false",
  1150.         "statusVal": "",
  1151.         "statusType": "auto",
  1152.         "x": 908.9522819519043,
  1153.         "y": 3155.7618000507355,
  1154.         "wires": []
  1155.     },
  1156.     {
  1157.         "id": "3adb4230412c1917",
  1158.         "type": "mysql",
  1159.         "z": "303c9862.ddfff8",
  1160.         "mydb": "76a2023f.8c254c",
  1161.         "name": "",
  1162.         "x": 1061.0952320098877,
  1163.         "y": 1429.562061548233,
  1164.         "wires": [
  1165.             [
  1166.                 "94c83d02.02f7c"
  1167.             ]
  1168.         ]
  1169.     },
  1170.     {
  1171.         "id": "f681de4bd9af482d",
  1172.         "type": "inject",
  1173.         "z": "303c9862.ddfff8",
  1174.         "name": "",
  1175.         "props": [
  1176.             {
  1177.                 "p": "payload"
  1178.             },
  1179.             {
  1180.                 "p": "topic",
  1181.                 "vt": "str"
  1182.             }
  1183.         ],
  1184.         "repeat": "3600",
  1185.         "crontab": "",
  1186.         "once": false,
  1187.         "onceDelay": 0.1,
  1188.         "topic": "",
  1189.         "payload": "",
  1190.         "payloadType": "date",
  1191.         "x": 521.0000228881836,
  1192.         "y": 361.9999599456787,
  1193.         "wires": [
  1194.             [
  1195.                 "c9e1512f88fd2f65"
  1196.             ]
  1197.         ]
  1198.     },
  1199.     {
  1200.         "id": "c9e1512f88fd2f65",
  1201.         "type": "function",
  1202.         "z": "303c9862.ddfff8",
  1203.         "name": "",
  1204.         "func": "const af = global.get('actionflows');\n\nconst resMsg = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: \"SELECT * FROM pv_rooftop\",\n    payload: [],\n});\n\nfor (const rooftop of resMsg.payload) {\n    node.send({\n        rooftopId: rooftop.id,\n        url: `https://api.solcast.com.au/rooftop_sites/${rooftop.solcast_rooftop_id}/forecasts?format=json&api_key=${flow.get('solcast_api_key')}`,\n        payload: null,\n    });\n}\n",
  1205.         "outputs": 1,
  1206.         "noerr": 0,
  1207.         "initialize": "",
  1208.         "finalize": "",
  1209.         "libs": [],
  1210.         "x": 674.0000228881836,
  1211.         "y": 365.9999599456787,
  1212.         "wires": [
  1213.             [
  1214.                 "bb1882e1d266048b",
  1215.                 "1bd4c55a0c9e9a52"
  1216.             ]
  1217.         ]
  1218.     },
  1219.     {
  1220.         "id": "bb1882e1d266048b",
  1221.         "type": "debug",
  1222.         "z": "303c9862.ddfff8",
  1223.         "name": "",
  1224.         "active": false,
  1225.         "tosidebar": true,
  1226.         "console": false,
  1227.         "tostatus": false,
  1228.         "complete": "true",
  1229.         "targetType": "full",
  1230.         "statusVal": "",
  1231.         "statusType": "auto",
  1232.         "x": 858.0000190734863,
  1233.         "y": 320.99995470046997,
  1234.         "wires": []
  1235.     },
  1236.     {
  1237.         "id": "e462500714e8288d",
  1238.         "type": "inject",
  1239.         "z": "303c9862.ddfff8",
  1240.         "name": "",
  1241.         "props": [
  1242.             {
  1243.                 "p": "payload"
  1244.             },
  1245.             {
  1246.                 "p": "topic",
  1247.                 "vt": "str"
  1248.             }
  1249.         ],
  1250.         "repeat": "",
  1251.         "crontab": "",
  1252.         "once": true,
  1253.         "onceDelay": 0.1,
  1254.         "topic": "",
  1255.         "payload": "",
  1256.         "payloadType": "date",
  1257.         "x": 558.0000877380371,
  1258.         "y": 144.00000286102295,
  1259.         "wires": [
  1260.             [
  1261.                 "ebdacb6214e094e7"
  1262.             ]
  1263.         ]
  1264.     },
  1265.     {
  1266.         "id": "ebdacb6214e094e7",
  1267.         "type": "function",
  1268.         "z": "303c9862.ddfff8",
  1269.         "name": "solcast API key",
  1270.         "func": "flow.set('solcast_api_key', '-A6JkZ-k_chTjbJxux9ATGFNT5GMAb7T')\nreturn msg;",
  1271.         "outputs": 1,
  1272.         "noerr": 0,
  1273.         "initialize": "",
  1274.         "finalize": "",
  1275.         "libs": [],
  1276.         "x": 775.0000915527344,
  1277.         "y": 149.00000286102295,
  1278.         "wires": [
  1279.             []
  1280.         ]
  1281.     },
  1282.     {
  1283.         "id": "1bd4c55a0c9e9a52",
  1284.         "type": "http request",
  1285.         "z": "303c9862.ddfff8",
  1286.         "name": "",
  1287.         "method": "GET",
  1288.         "ret": "obj",
  1289.         "paytoqs": "ignore",
  1290.         "url": "",
  1291.         "tls": "",
  1292.         "persist": false,
  1293.         "proxy": "",
  1294.         "authType": "",
  1295.         "senderr": false,
  1296.         "x": 871.0000190734863,
  1297.         "y": 366.99995613098145,
  1298.         "wires": [
  1299.             [
  1300.                 "b7d2296ea2d6e9e1",
  1301.                 "c4597790.33e678"
  1302.             ]
  1303.         ]
  1304.     },
  1305.     {
  1306.         "id": "b7d2296ea2d6e9e1",
  1307.         "type": "debug",
  1308.         "z": "303c9862.ddfff8",
  1309.         "name": "",
  1310.         "active": false,
  1311.         "tosidebar": true,
  1312.         "console": false,
  1313.         "tostatus": false,
  1314.         "complete": "true",
  1315.         "targetType": "full",
  1316.         "statusVal": "",
  1317.         "statusType": "auto",
  1318.         "x": 1050.0000228881836,
  1319.         "y": 320.99995517730713,
  1320.         "wires": []
  1321.     },
  1322.     {
  1323.         "id": "12df0984d0ecf73c",
  1324.         "type": "debug",
  1325.         "z": "303c9862.ddfff8",
  1326.         "name": "",
  1327.         "active": false,
  1328.         "tosidebar": true,
  1329.         "console": false,
  1330.         "tostatus": false,
  1331.         "complete": "true",
  1332.         "targetType": "full",
  1333.         "statusVal": "",
  1334.         "statusType": "auto",
  1335.         "x": 1255.0000228881836,
  1336.         "y": 327.9999542236328,
  1337.         "wires": []
  1338.     },
  1339.     {
  1340.         "id": "77d073a993526f05",
  1341.         "type": "comment",
  1342.         "z": "303c9862.ddfff8",
  1343.         "name": "forecast UI",
  1344.         "info": "",
  1345.         "x": 271.6666603088379,
  1346.         "y": 803.1333267688751,
  1347.         "wires": []
  1348.     },
  1349.     {
  1350.         "id": "b4e9aadd8bfac91b",
  1351.         "type": "function",
  1352.         "z": "303c9862.ddfff8",
  1353.         "name": "stats",
  1354.         "func": "msg.stats = {\n    table: 'pv_inverter_battery_soc_prediction',\n    column_date: 'log_date',\n    column_value: 'soc',\n    interval: '0 HOUR',\n    agregateFunction: 'AVG',\n    where: `1`,\n}\nreturn msg;",
  1355.         "outputs": 1,
  1356.         "noerr": 0,
  1357.         "initialize": "",
  1358.         "finalize": "",
  1359.         "libs": [],
  1360.         "x": 1547.6666259765625,
  1361.         "y": 3258.333241701126,
  1362.         "wires": [
  1363.             [
  1364.                 "d124319fb087e0e4"
  1365.             ]
  1366.         ]
  1367.     },
  1368.     {
  1369.         "id": "d124319fb087e0e4",
  1370.         "type": "subflow:9c5e9d61.1f5fe",
  1371.         "z": "303c9862.ddfff8",
  1372.         "name": "",
  1373.         "env": [],
  1374.         "x": 1722.6665420532227,
  1375.         "y": 3258.3332846164703,
  1376.         "wires": [
  1377.             [
  1378.                 "a0bc64c52698e9d4",
  1379.                 "e19475402f3c5f96"
  1380.             ]
  1381.         ]
  1382.     },
  1383.     {
  1384.         "id": "8ce17d64b220885e",
  1385.         "type": "ui_chart",
  1386.         "z": "303c9862.ddfff8",
  1387.         "name": "",
  1388.         "group": "d1c39829.3ee0e8",
  1389.         "order": 8,
  1390.         "width": 0,
  1391.         "height": 0,
  1392.         "label": "Previous 12 / Next 36 Hours",
  1393.         "chartType": "line",
  1394.         "legend": "false",
  1395.         "xformat": "HH:mm:ss",
  1396.         "interpolate": "bezier",
  1397.         "nodata": "",
  1398.         "dot": false,
  1399.         "ymin": "0",
  1400.         "ymax": "100",
  1401.         "removeOlder": "1",
  1402.         "removeOlderPoints": "",
  1403.         "removeOlderUnit": "86400",
  1404.         "cutout": 0,
  1405.         "useOneColor": false,
  1406.         "useUTC": false,
  1407.         "colors": [
  1408.             "#00a1d6",
  1409.             "#ffcd42",
  1410.             "#ff7f0e",
  1411.             "#2ca02c",
  1412.             "#98df8a",
  1413.             "#d62728",
  1414.             "#ff9896",
  1415.             "#9467bd",
  1416.             "#c5b0d5"
  1417.         ],
  1418.         "outputs": 1,
  1419.         "x": 2306.666175842285,
  1420.         "y": 3353.3332788944244,
  1421.         "wires": [
  1422.             []
  1423.         ]
  1424.     },
  1425.     {
  1426.         "id": "a0bc64c52698e9d4",
  1427.         "type": "debug",
  1428.         "z": "303c9862.ddfff8",
  1429.         "name": "",
  1430.         "active": false,
  1431.         "tosidebar": true,
  1432.         "console": false,
  1433.         "tostatus": false,
  1434.         "complete": "false",
  1435.         "statusVal": "",
  1436.         "statusType": "auto",
  1437.         "x": 1810.666633605957,
  1438.         "y": 3150.333283662796,
  1439.         "wires": []
  1440.     },
  1441.     {
  1442.         "id": "d799d8e48807dbf8",
  1443.         "type": "function",
  1444.         "z": "303c9862.ddfff8",
  1445.         "name": "stats",
  1446.         "func": "msg.stats = {\n    table: 'pv_inverter_record',\n    column_date: 'log_date',\n    column_value: 'battery_soc',\n    interval: '12 HOUR',\n    agregateFunction: 'AVG',\n    where: `1`,\n}\nreturn msg;",
  1447.         "outputs": 1,
  1448.         "noerr": 0,
  1449.         "initialize": "",
  1450.         "finalize": "",
  1451.         "libs": [],
  1452.         "x": 1014.6666259765625,
  1453.         "y": 3234.733243227005,
  1454.         "wires": [
  1455.             [
  1456.                 "2f129c7156b55337"
  1457.             ]
  1458.         ]
  1459.     },
  1460.     {
  1461.         "id": "2f129c7156b55337",
  1462.         "type": "subflow:9c5e9d61.1f5fe",
  1463.         "z": "303c9862.ddfff8",
  1464.         "name": "",
  1465.         "env": [],
  1466.         "x": 1189.6665420532227,
  1467.         "y": 3234.7332861423492,
  1468.         "wires": [
  1469.             [
  1470.                 "4763f74abca7dfc6",
  1471.                 "275ea24681bc6eba"
  1472.             ]
  1473.         ]
  1474.     },
  1475.     {
  1476.         "id": "5a2e9bf1bddbfbb7",
  1477.         "type": "inject",
  1478.         "z": "303c9862.ddfff8",
  1479.         "name": "",
  1480.         "props": [
  1481.             {
  1482.                 "p": "payload"
  1483.             },
  1484.             {
  1485.                 "p": "topic",
  1486.                 "vt": "str"
  1487.             }
  1488.         ],
  1489.         "repeat": "",
  1490.         "crontab": "",
  1491.         "once": false,
  1492.         "onceDelay": 0.1,
  1493.         "topic": "",
  1494.         "payload": "",
  1495.         "payloadType": "date",
  1496.         "x": 889.666618347168,
  1497.         "y": 3316.7332422733307,
  1498.         "wires": [
  1499.             [
  1500.                 "d799d8e48807dbf8"
  1501.             ]
  1502.         ]
  1503.     },
  1504.     {
  1505.         "id": "4763f74abca7dfc6",
  1506.         "type": "debug",
  1507.         "z": "303c9862.ddfff8",
  1508.         "name": "",
  1509.         "active": false,
  1510.         "tosidebar": true,
  1511.         "console": false,
  1512.         "tostatus": false,
  1513.         "complete": "false",
  1514.         "statusVal": "",
  1515.         "statusType": "auto",
  1516.         "x": 1385.6666374206543,
  1517.         "y": 3197.7332842350006,
  1518.         "wires": []
  1519.     },
  1520.     {
  1521.         "id": "275ea24681bc6eba",
  1522.         "type": "function",
  1523.         "z": "303c9862.ddfff8",
  1524.         "name": "",
  1525.         "func": "msg.stats1 = msg.payload;\nreturn msg;",
  1526.         "outputs": 1,
  1527.         "noerr": 0,
  1528.         "initialize": "",
  1529.         "finalize": "",
  1530.         "libs": [],
  1531.         "x": 1384.6666374206543,
  1532.         "y": 3273.733285188675,
  1533.         "wires": [
  1534.             [
  1535.                 "b4e9aadd8bfac91b"
  1536.             ]
  1537.         ]
  1538.     },
  1539.     {
  1540.         "id": "50a7249feade58d6",
  1541.         "type": "http in",
  1542.         "z": "303c9862.ddfff8",
  1543.         "name": "",
  1544.         "url": "/pylon-pv-battery-state-record",
  1545.         "method": "post",
  1546.         "upload": false,
  1547.         "swaggerDoc": "",
  1548.         "x": 502.66661071777344,
  1549.         "y": 3595.333428621292,
  1550.         "wires": [
  1551.             [
  1552.                 "e9349d108908ac06",
  1553.                 "da499acd5b31d569",
  1554.                 "1c67febfe1ce1abd",
  1555.                 "2c54ae5b8ba85505"
  1556.             ]
  1557.         ]
  1558.     },
  1559.     {
  1560.         "id": "e9349d108908ac06",
  1561.         "type": "debug",
  1562.         "z": "303c9862.ddfff8",
  1563.         "name": "",
  1564.         "active": false,
  1565.         "tosidebar": true,
  1566.         "console": false,
  1567.         "tostatus": false,
  1568.         "complete": "false",
  1569.         "statusVal": "",
  1570.         "statusType": "auto",
  1571.         "x": 863.8666381835938,
  1572.         "y": 3537.733472108841,
  1573.         "wires": []
  1574.     },
  1575.     {
  1576.         "id": "da499acd5b31d569",
  1577.         "type": "function",
  1578.         "z": "303c9862.ddfff8",
  1579.         "name": "",
  1580.         "func": "msg.payload = {status: 200}\nreturn msg;",
  1581.         "outputs": 1,
  1582.         "noerr": 0,
  1583.         "initialize": "",
  1584.         "finalize": "",
  1585.         "libs": [
  1586.             {
  1587.                 "var": "fs",
  1588.                 "module": "fs"
  1589.             }
  1590.         ],
  1591.         "x": 847.8666305541992,
  1592.         "y": 3602.7334740161896,
  1593.         "wires": [
  1594.             [
  1595.                 "e0665fb2fd857050"
  1596.             ]
  1597.         ]
  1598.     },
  1599.     {
  1600.         "id": "e0665fb2fd857050",
  1601.         "type": "http response",
  1602.         "z": "303c9862.ddfff8",
  1603.         "name": "",
  1604.         "statusCode": "",
  1605.         "headers": {},
  1606.         "x": 1022.8666362762451,
  1607.         "y": 3610.7334740161896,
  1608.         "wires": []
  1609.     },
  1610.     {
  1611.         "id": "5f18183f56309951",
  1612.         "type": "function",
  1613.         "z": "303c9862.ddfff8",
  1614.         "name": "disp_pwr",
  1615.         "func": "if (msg.payload.type == \"disp_pwr\") {\n    msg.payload = msg.payload.data;\n        \n    const columns = Object.keys(msg.payload).map((column) => `${column} = ?`).join(', ');\n\n    msg.topic = `INSERT INTO pv_battery_state_record SET log_date = NOW(), ${columns}`;\n    msg.payload = Object.values(msg.payload);\n\n    return msg;\n}\n",
  1616.         "outputs": 1,
  1617.         "noerr": 0,
  1618.         "initialize": "",
  1619.         "finalize": "",
  1620.         "libs": [],
  1621.         "x": 1640.8665504455566,
  1622.         "y": 3551.3337955474854,
  1623.         "wires": [
  1624.             [
  1625.                 "d21bbf6f218abd58"
  1626.             ]
  1627.         ]
  1628.     },
  1629.     {
  1630.         "id": "d21bbf6f218abd58",
  1631.         "type": "mysql",
  1632.         "z": "303c9862.ddfff8",
  1633.         "mydb": "76a2023f.8c254c",
  1634.         "name": "",
  1635.         "x": 1800.8665618896484,
  1636.         "y": 3547.5339879989624,
  1637.         "wires": [
  1638.             [
  1639.                 "d7de585efcc764fc"
  1640.             ]
  1641.         ]
  1642.     },
  1643.     {
  1644.         "id": "412378114f768b4a",
  1645.         "type": "comment",
  1646.         "z": "303c9862.ddfff8",
  1647.         "name": "pylon logger",
  1648.         "info": "",
  1649.         "x": 301.1666145324707,
  1650.         "y": 3486.333427667618,
  1651.         "wires": []
  1652.     },
  1653.     {
  1654.         "id": "403733106671b049",
  1655.         "type": "function",
  1656.         "z": "303c9862.ddfff8",
  1657.         "name": "",
  1658.         "func": "msg.batteryMode = msg.payload.batteryMode;\nmsg.payload = msg.payload.batteryPower;\nmsg.topic = msg.batteryMode;\n\nreturn msg;",
  1659.         "outputs": 1,
  1660.         "noerr": 0,
  1661.         "initialize": "",
  1662.         "finalize": "",
  1663.         "libs": [],
  1664.         "x": 1212.6666259765625,
  1665.         "y": 3857.333382844925,
  1666.         "wires": [
  1667.             [
  1668.                 "8c7b70b7d47684f3",
  1669.                 "d971f7c5cdcd8c7e",
  1670.                 "189adeb07b8246d6"
  1671.             ]
  1672.         ]
  1673.     },
  1674.     {
  1675.         "id": "8c7b70b7d47684f3",
  1676.         "type": "ui_gauge",
  1677.         "z": "303c9862.ddfff8",
  1678.         "d": true,
  1679.         "name": "Battery",
  1680.         "group": "819686be.5e6a58",
  1681.         "order": 4,
  1682.         "width": 0,
  1683.         "height": 0,
  1684.         "gtype": "gage",
  1685.         "title": "BMS Battery - {{msg.batteryMode}}",
  1686.         "label": "W",
  1687.         "format": "{{value}}",
  1688.         "min": "-8000",
  1689.         "max": "8000",
  1690.         "colors": [
  1691.             "#b30000",
  1692.             "#ffffff",
  1693.             "#36cc00"
  1694.         ],
  1695.         "seg1": "",
  1696.         "seg2": "",
  1697.         "x": 1438.6666526794434,
  1698.         "y": 3791.333286523819,
  1699.         "wires": []
  1700.     },
  1701.     {
  1702.         "id": "be5efd648227638b",
  1703.         "type": "function",
  1704.         "z": "303c9862.ddfff8",
  1705.         "name": "no data timeout",
  1706.         "func": "const timeout = 30000;\n\nconst timer = context.get('timer');\n\nif (timer) {\n    clearTimeout(timer);\n}\n\ncontext.set('timer', setTimeout(() => {\n    node.send({\n        bmsDataTimedout: true,\n        payload: {},\n    })\n}, timeout));\n\nnode.send(msg);",
  1707.         "outputs": 1,
  1708.         "noerr": 0,
  1709.         "initialize": "",
  1710.         "finalize": "",
  1711.         "libs": [],
  1712.         "x": 981.6666259765625,
  1713.         "y": 3802.733381509781,
  1714.         "wires": [
  1715.             [
  1716.                 "6e138e18de8b9465"
  1717.             ]
  1718.         ]
  1719.     },
  1720.     {
  1721.         "id": "c3f6137434653aca",
  1722.         "type": "function",
  1723.         "z": "303c9862.ddfff8",
  1724.         "name": "no data timeout",
  1725.         "func": "const timeout = 30000;\n\nconst timer = context.get('timer');\n\nif (timer) {\n    clearTimeout(timer);\n}\n\ncontext.set('timer', setTimeout(() => {\n    node.send({\n        inverterDataTimedout: true,\n        payload: {},\n    })\n}, timeout));\n\nnode.send(msg);",
  1726.         "outputs": 1,
  1727.         "noerr": 0,
  1728.         "initialize": "",
  1729.         "finalize": "",
  1730.         "libs": [],
  1731.         "x": 701.6666450500488,
  1732.         "y": 1563.7333748340607,
  1733.         "wires": [
  1734.             [
  1735.                 "efdc183d.d80958",
  1736.                 "4dad62b3.f306fc",
  1737.                 "9c95fb95.b18c28",
  1738.                 "3aab581b.326c98",
  1739.                 "a599f9f6a477953a",
  1740.                 "d886e6cc2138ed94",
  1741.                 "6e138e18de8b9465",
  1742.                 "f2911eb919633a36"
  1743.             ]
  1744.         ]
  1745.     },
  1746.     {
  1747.         "id": "fe9df8fcb1931c40",
  1748.         "type": "function",
  1749.         "z": "303c9862.ddfff8",
  1750.         "name": "siren beep",
  1751.         "func": "msg.payload = [\"smarthouse-controller-siren\", \"pin.siren.pulse.start\", {\n    \"sequence\" : \"beep\",\n}]\n\nreturn msg;",
  1752.         "outputs": 1,
  1753.         "noerr": 0,
  1754.         "initialize": "",
  1755.         "finalize": "",
  1756.         "libs": [],
  1757.         "x": 1279.6666984558105,
  1758.         "y": 2749.9334642887115,
  1759.         "wires": [
  1760.             [
  1761.                 "d085d90cf23540f8"
  1762.             ]
  1763.         ]
  1764.     },
  1765.     {
  1766.         "id": "d085d90cf23540f8",
  1767.         "type": "jsonrpc-call",
  1768.         "z": "303c9862.ddfff8",
  1769.         "name": "",
  1770.         "method": "rpc.proxy",
  1771.         "client": "36ca1673.3376ea",
  1772.         "x": 1443.6666564941406,
  1773.         "y": 2749.9333679676056,
  1774.         "wires": [
  1775.             [
  1776.                 "7b2db10fc138600d"
  1777.             ]
  1778.         ]
  1779.     },
  1780.     {
  1781.         "id": "11b3b99f4f114d05",
  1782.         "type": "inject",
  1783.         "z": "303c9862.ddfff8",
  1784.         "name": "",
  1785.         "props": [
  1786.             {
  1787.                 "p": "payload"
  1788.             },
  1789.             {
  1790.                 "p": "topic",
  1791.                 "vt": "str"
  1792.             }
  1793.         ],
  1794.         "repeat": "",
  1795.         "crontab": "",
  1796.         "once": false,
  1797.         "onceDelay": 0.1,
  1798.         "topic": "",
  1799.         "payload": "",
  1800.         "payloadType": "date",
  1801.         "x": 1115.6668395996094,
  1802.         "y": 2787.9336347579956,
  1803.         "wires": [
  1804.             [
  1805.                 "fe9df8fcb1931c40"
  1806.             ]
  1807.         ]
  1808.     },
  1809.     {
  1810.         "id": "7b2db10fc138600d",
  1811.         "type": "debug",
  1812.         "z": "303c9862.ddfff8",
  1813.         "name": "",
  1814.         "active": false,
  1815.         "tosidebar": true,
  1816.         "console": false,
  1817.         "tostatus": false,
  1818.         "complete": "false",
  1819.         "statusVal": "",
  1820.         "statusType": "auto",
  1821.         "x": 1607.4666557312012,
  1822.         "y": 2751.933173418045,
  1823.         "wires": []
  1824.     },
  1825.     {
  1826.         "id": "a599f9f6a477953a",
  1827.         "type": "function",
  1828.         "z": "303c9862.ddfff8",
  1829.         "name": "Přetížení fáze siréna",
  1830.         "func": "const phasesLoad = [\n    Math.round(100 * msg.payload.backup_p1 / 3300),\n    Math.round(100 * msg.payload.backup_p2 / 3300),\n    Math.round(100 * msg.payload.backup_p3 / 3300),\n];\n\nconst maxLoad = 120;\n\nif (phasesLoad[0] >= maxLoad || phasesLoad[1] >= maxLoad || phasesLoad[2] >= maxLoad) {\n    msg.payload = phasesLoad;\n    return msg;\n}\n",
  1831.         "outputs": 1,
  1832.         "noerr": 0,
  1833.         "initialize": "",
  1834.         "finalize": "",
  1835.         "libs": [],
  1836.         "x": 903.6666946411133,
  1837.         "y": 2716.9332687854767,
  1838.         "wires": [
  1839.             [
  1840.                 "e7e97e769bab9c97"
  1841.             ]
  1842.         ]
  1843.     },
  1844.     {
  1845.         "id": "e7e97e769bab9c97",
  1846.         "type": "throttle",
  1847.         "z": "303c9862.ddfff8",
  1848.         "name": "",
  1849.         "throttleType": "time",
  1850.         "timeLimit": "5",
  1851.         "timeLimitType": "seconds",
  1852.         "countLimit": 0,
  1853.         "blockSize": 0,
  1854.         "locked": false,
  1855.         "x": 1114.6666946411133,
  1856.         "y": 2720.7336599826813,
  1857.         "wires": [
  1858.             [
  1859.                 "fe9df8fcb1931c40",
  1860.                 "42545386709df21d"
  1861.             ]
  1862.         ]
  1863.     },
  1864.     {
  1865.         "id": "42545386709df21d",
  1866.         "type": "debug",
  1867.         "z": "303c9862.ddfff8",
  1868.         "name": "",
  1869.         "active": false,
  1870.         "tosidebar": true,
  1871.         "console": false,
  1872.         "tostatus": false,
  1873.         "complete": "false",
  1874.         "statusVal": "",
  1875.         "statusType": "auto",
  1876.         "x": 1292.666696548462,
  1877.         "y": 2683.733462572098,
  1878.         "wires": []
  1879.     },
  1880.     {
  1881.         "id": "a99792fb08391c11",
  1882.         "type": "function",
  1883.         "z": "303c9862.ddfff8",
  1884.         "name": "clear",
  1885.         "func": "msg.payload = [];\nreturn msg;",
  1886.         "outputs": 1,
  1887.         "noerr": 0,
  1888.         "initialize": "",
  1889.         "finalize": "",
  1890.         "libs": [],
  1891.         "x": 1456.2666206359863,
  1892.         "y": 3349.7332861423492,
  1893.         "wires": [
  1894.             [
  1895.                 "8ce17d64b220885e"
  1896.             ]
  1897.         ]
  1898.     },
  1899.     {
  1900.         "id": "81a374d23a95621c",
  1901.         "type": "function",
  1902.         "z": "303c9862.ddfff8",
  1903.         "name": "",
  1904.         "func": "\nreturn msg;",
  1905.         "outputs": 1,
  1906.         "noerr": 0,
  1907.         "initialize": "",
  1908.         "finalize": "",
  1909.         "libs": [],
  1910.         "x": 649.2666015625,
  1911.         "y": 3217.73328435421,
  1912.         "wires": [
  1913.             [
  1914.                 "2b8e5315.f38d9c",
  1915.                 "a99792fb08391c11"
  1916.             ]
  1917.         ]
  1918.     },
  1919.     {
  1920.         "id": "d886e6cc2138ed94",
  1921.         "type": "function",
  1922.         "z": "303c9862.ddfff8",
  1923.         "name": "Příliž nízký stav baterie",
  1924.         "func": "const soc = msg.payload.battery_soc;\nlet lastNotifiedLowBatterySOC = flow.get(`lastNotifiedLowBatterySOC`) || 100;\n\nconst pvInverterRecord = flow.get('pvInverterRecord');\n\nif (pvInverterRecord.grid_mode_label != \"Connected to grid\") {\n    if (soc <= 15) {\n        // pokud klesnul stav baterie tak pipnout\n        if (soc < lastNotifiedLowBatterySOC) {\n            node.send(msg);\n        }\n    }\n}\n\nflow.set(`lastNotifiedLowBatterySOC`, soc);\n",
  1925.         "outputs": 1,
  1926.         "noerr": 0,
  1927.         "initialize": "",
  1928.         "finalize": "",
  1929.         "libs": [],
  1930.         "x": 916.6666564941406,
  1931.         "y": 2760.13334274292,
  1932.         "wires": [
  1933.             [
  1934.                 "fe9df8fcb1931c40"
  1935.             ]
  1936.         ]
  1937.     },
  1938.     {
  1939.         "id": "d707a23a58a83b39",
  1940.         "type": "ui_chart",
  1941.         "z": "303c9862.ddfff8",
  1942.         "name": "House Consumption",
  1943.         "group": "819686be.5e6a58",
  1944.         "order": 2,
  1945.         "width": 0,
  1946.         "height": 0,
  1947.         "label": "House Consumption: {{msg.payload}}W",
  1948.         "chartType": "line",
  1949.         "legend": "false",
  1950.         "xformat": "HH:mm:ss",
  1951.         "interpolate": "linear",
  1952.         "nodata": "",
  1953.         "dot": false,
  1954.         "ymin": "0",
  1955.         "ymax": "",
  1956.         "removeOlder": "5",
  1957.         "removeOlderPoints": "",
  1958.         "removeOlderUnit": "60",
  1959.         "cutout": 0,
  1960.         "useOneColor": false,
  1961.         "useUTC": false,
  1962.         "colors": [
  1963.             "#1f77b4",
  1964.             "#aec7e8",
  1965.             "#ff7f0e",
  1966.             "#2ca02c",
  1967.             "#98df8a",
  1968.             "#d62728",
  1969.             "#ff9896",
  1970.             "#9467bd",
  1971.             "#c5b0d5"
  1972.         ],
  1973.         "outputs": 1,
  1974.         "x": 1420.6666564941406,
  1975.         "y": 2197.933350801468,
  1976.         "wires": [
  1977.             []
  1978.         ]
  1979.     },
  1980.     {
  1981.         "id": "7a1b5a43ecb17018",
  1982.         "type": "function",
  1983.         "z": "303c9862.ddfff8",
  1984.         "name": "",
  1985.         "func": "msg.payload = msg.payload.ac_house_consumption_maxof;\nreturn msg;",
  1986.         "outputs": 1,
  1987.         "noerr": 0,
  1988.         "initialize": "",
  1989.         "finalize": "",
  1990.         "libs": [],
  1991.         "x": 1233.666633605957,
  1992.         "y": 2201.333331346512,
  1993.         "wires": [
  1994.             [
  1995.                 "d707a23a58a83b39"
  1996.             ]
  1997.         ]
  1998.     },
  1999.     {
  2000.         "id": "d971f7c5cdcd8c7e",
  2001.         "type": "ui_text",
  2002.         "z": "303c9862.ddfff8",
  2003.         "group": "312b72db.d7629e",
  2004.         "order": 4,
  2005.         "width": 0,
  2006.         "height": 0,
  2007.         "name": "Battery",
  2008.         "label": "Battery ({{msg.batteryMode}})",
  2009.         "format": "{{msg.payload}}W",
  2010.         "layout": "row-spread",
  2011.         "className": "",
  2012.         "x": 1452.6666564941406,
  2013.         "y": 3933.133287668228,
  2014.         "wires": []
  2015.     },
  2016.     {
  2017.         "id": "b3a8bf6e77ec851a",
  2018.         "type": "inject",
  2019.         "z": "303c9862.ddfff8",
  2020.         "name": "",
  2021.         "props": [
  2022.             {
  2023.                 "p": "payload"
  2024.             },
  2025.             {
  2026.                 "p": "todayOffset",
  2027.                 "v": "0",
  2028.                 "vt": "str"
  2029.             }
  2030.         ],
  2031.         "repeat": "600",
  2032.         "crontab": "",
  2033.         "once": true,
  2034.         "onceDelay": 0.1,
  2035.         "topic": "",
  2036.         "payload": "",
  2037.         "payloadType": "date",
  2038.         "x": 481.66664123535156,
  2039.         "y": 4236.133440256119,
  2040.         "wires": [
  2041.             [
  2042.                 "d96eb2fb252d9ecc"
  2043.             ]
  2044.         ]
  2045.     },
  2046.     {
  2047.         "id": "d96eb2fb252d9ecc",
  2048.         "type": "function",
  2049.         "z": "303c9862.ddfff8",
  2050.         "name": "",
  2051.         "func": "const af = global.get('actionflows');\n\nnode.status({text: `Start`});\n\nconst resDb = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT MAX(pv_harvested_kwh1) AS kwh1, MAX(pv_harvested_kwh2) AS kwh2 FROM (\n            SELECT time, total_ws1/1000/3600 AS pv_harvested_kwh1, total_ws2/1000/3600 AS pv_harvested_kwh2 FROM (\n            \tSELECT time, @curr1:=power_value1*(IF(@prevT IS NULL, 0, time-@prevT)), @curr2:=power_value2*(IF(@prevT IS NULL, 0, time-@prevT)) as ws, @e1:=@e1+@curr1 AS total_ws1, @e2:=@e2+@curr2 AS total_ws2, @prevT:=time FROM (\n            \t\tSELECT\n            \t\tUNIX_TIMESTAMP(log_date) AS \"time\",\n            \t\tpv_ppv1 AS power_value1,\n\t\t\t\t\tpv_ppv2 AS power_value2\n            \t\tFROM pv_inverter_record\n            \t\tWHERE\n            \t\tlog_date BETWEEN DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) AND DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) + INTERVAL 1 DAY\n            \t\tORDER BY log_date\n            \t)r1, (SELECT@e1:=0)e1, (SELECT@e2:=0)e2, (SELECT@prevT:=NULL)prevT\n            )r2\n        )r3\n    `,\n    values: [],\n});\n\nnode.status({text: `Done`});\n\nmsg.payload = resDb.payload[0];\n\nmsg.dailyStats = {\n\tpv1: msg.payload.kwh1,\n\tpv2: msg.payload.kwh2,\n\tpv: msg.payload.kwh1 + msg.payload.kwh2,\n};\n\nreturn msg;\n",
  2052.         "outputs": 1,
  2053.         "noerr": 0,
  2054.         "initialize": "",
  2055.         "finalize": "",
  2056.         "libs": [],
  2057.         "x": 634.666633605957,
  2058.         "y": 4253.133419275284,
  2059.         "wires": [
  2060.             [
  2061.                 "6a115fdb62bf4b36",
  2062.                 "3e1392c885cd773c",
  2063.                 "ce47cc5f7bbcaeed"
  2064.             ]
  2065.         ]
  2066.     },
  2067.     {
  2068.         "id": "3e1392c885cd773c",
  2069.         "type": "debug",
  2070.         "z": "303c9862.ddfff8",
  2071.         "name": "",
  2072.         "active": false,
  2073.         "tosidebar": true,
  2074.         "console": false,
  2075.         "tostatus": false,
  2076.         "complete": "false",
  2077.         "statusVal": "",
  2078.         "statusType": "auto",
  2079.         "x": 830.6666450500488,
  2080.         "y": 4221.1332924366,
  2081.         "wires": []
  2082.     },
  2083.     {
  2084.         "id": "8c846f474c9fbc76",
  2085.         "type": "ui_text",
  2086.         "z": "303c9862.ddfff8",
  2087.         "group": "802549dc0ec48149",
  2088.         "order": 1,
  2089.         "width": 0,
  2090.         "height": 0,
  2091.         "name": "",
  2092.         "label": "Harvested Today",
  2093.         "format": "{{msg.payload.total}}kWh ({{msg.payload.total1}}+{{msg.payload.total2}})",
  2094.         "layout": "row-spread",
  2095.         "className": "",
  2096.         "x": 1184.6666526794434,
  2097.         "y": 4273.133440256119,
  2098.         "wires": []
  2099.     },
  2100.     {
  2101.         "id": "61bba1d29f469c59",
  2102.         "type": "comment",
  2103.         "z": "303c9862.ddfff8",
  2104.         "name": "stats",
  2105.         "info": "",
  2106.         "x": 271.4666519165039,
  2107.         "y": 4097.133380174637,
  2108.         "wires": []
  2109.     },
  2110.     {
  2111.         "id": "6a115fdb62bf4b36",
  2112.         "type": "function",
  2113.         "z": "303c9862.ddfff8",
  2114.         "name": "",
  2115.         "func": "const msgs = [null, null];\n\nmsgs[msg.todayOffset] = {\n    payload: {\n        total: Math.round((msg.payload.kwh1 + msg.payload.kwh2) * 10) / 10,\n        total1: Math.round(msg.payload.kwh1 * 10) / 10,\n        total2: Math.round(msg.payload.kwh2 * 10) / 10,\n    }\n}\n\nreturn msgs;",
  2116.         "outputs": 2,
  2117.         "noerr": 0,
  2118.         "initialize": "",
  2119.         "finalize": "",
  2120.         "libs": [],
  2121.         "x": 990.6666488647461,
  2122.         "y": 4275.9334881305695,
  2123.         "wires": [
  2124.             [
  2125.                 "8c846f474c9fbc76"
  2126.             ],
  2127.             [
  2128.                 "078d27b844fc3fae"
  2129.             ]
  2130.         ]
  2131.     },
  2132.     {
  2133.         "id": "51ab74349f8c4265",
  2134.         "type": "ui_text",
  2135.         "z": "303c9862.ddfff8",
  2136.         "group": "802549dc0ec48149",
  2137.         "order": 2,
  2138.         "width": 0,
  2139.         "height": 0,
  2140.         "name": "",
  2141.         "label": "Consumed Today",
  2142.         "format": "{{msg.payload}}kWh",
  2143.         "layout": "row-spread",
  2144.         "x": 1197.666648864746,
  2145.         "y": 4499.733881235123,
  2146.         "wires": []
  2147.     },
  2148.     {
  2149.         "id": "078d27b844fc3fae",
  2150.         "type": "ui_text",
  2151.         "z": "303c9862.ddfff8",
  2152.         "group": "802549dc0ec48149",
  2153.         "order": 3,
  2154.         "width": 0,
  2155.         "height": 0,
  2156.         "name": "",
  2157.         "label": "Harvested Yesterday",
  2158.         "format": "{{msg.payload.total}}kWh ({{msg.payload.total1}}+{{msg.payload.total2}})",
  2159.         "layout": "row-spread",
  2160.         "className": "",
  2161.         "x": 1190.666633605957,
  2162.         "y": 4310.733233690262,
  2163.         "wires": []
  2164.     },
  2165.     {
  2166.         "id": "9718c4e32e7b3791",
  2167.         "type": "inject",
  2168.         "z": "303c9862.ddfff8",
  2169.         "name": "",
  2170.         "props": [
  2171.             {
  2172.                 "p": "payload"
  2173.             },
  2174.             {
  2175.                 "p": "todayOffset",
  2176.                 "v": "1",
  2177.                 "vt": "str"
  2178.             }
  2179.         ],
  2180.         "repeat": "600",
  2181.         "crontab": "",
  2182.         "once": true,
  2183.         "onceDelay": 0.1,
  2184.         "topic": "",
  2185.         "payload": "",
  2186.         "payloadType": "date",
  2187.         "x": 478.66664123535156,
  2188.         "y": 4290.3333904743195,
  2189.         "wires": [
  2190.             [
  2191.                 "d96eb2fb252d9ecc"
  2192.             ]
  2193.         ]
  2194.     },
  2195.     {
  2196.         "id": "f71446e6f06a56d9",
  2197.         "type": "inject",
  2198.         "z": "303c9862.ddfff8",
  2199.         "name": "",
  2200.         "props": [
  2201.             {
  2202.                 "p": "payload"
  2203.             },
  2204.             {
  2205.                 "p": "todayOffset",
  2206.                 "v": "0",
  2207.                 "vt": "str"
  2208.             }
  2209.         ],
  2210.         "repeat": "600",
  2211.         "crontab": "",
  2212.         "once": true,
  2213.         "onceDelay": 0.1,
  2214.         "topic": "",
  2215.         "payload": "",
  2216.         "payloadType": "date",
  2217.         "x": 475.66663360595703,
  2218.         "y": 4461.933429002762,
  2219.         "wires": [
  2220.             [
  2221.                 "478f152e86f5bbaa"
  2222.             ]
  2223.         ]
  2224.     },
  2225.     {
  2226.         "id": "3b778125dfe6d052",
  2227.         "type": "inject",
  2228.         "z": "303c9862.ddfff8",
  2229.         "name": "",
  2230.         "props": [
  2231.             {
  2232.                 "p": "payload"
  2233.             },
  2234.             {
  2235.                 "p": "todayOffset",
  2236.                 "v": "1",
  2237.                 "vt": "str"
  2238.             }
  2239.         ],
  2240.         "repeat": "600",
  2241.         "crontab": "",
  2242.         "once": true,
  2243.         "onceDelay": 0.1,
  2244.         "topic": "",
  2245.         "payload": "",
  2246.         "payloadType": "date",
  2247.         "x": 472.66663360595703,
  2248.         "y": 4516.1333792209625,
  2249.         "wires": [
  2250.             [
  2251.                 "478f152e86f5bbaa"
  2252.             ]
  2253.         ]
  2254.     },
  2255.     {
  2256.         "id": "478f152e86f5bbaa",
  2257.         "type": "function",
  2258.         "z": "303c9862.ddfff8",
  2259.         "name": "",
  2260.         "func": "const af = global.get('actionflows');\n\nnode.status({text: `Start`});\n\nconst resDb = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT MAX(house_consumption_kwh) AS kwh FROM (\n            SELECT time, total_ws/1000/3600 AS house_consumption_kwh FROM (\n            \tSELECT time, @curr:=power_value*(IF(@prevT IS NULL, 0, time-@prevT)) as ws, @e:=@e+@curr AS total_ws, @prevT:=time FROM (\n            \t\tSELECT\n            \t\tUNIX_TIMESTAMP(log_date) AS \"time\",\n            \t\tGREATEST((IF(work_mode_label = \"Normal (On-Grid)\", ac_load_p1, 0)+IF(work_mode_label = \"Normal (On-Grid)\", ac_load_p2, 0)+IF(work_mode_label = \"Normal (On-Grid)\", ac_load_p3, 0)) + (backup_p1+backup_p2+backup_p3), ac_house_consumption) AS power_value\n            \t\tFROM pv_inverter_record\n            \t\tWHERE\n            \t\tlog_date BETWEEN DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) AND DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) + INTERVAL 1 DAY\n            \t\tORDER BY log_date\n            \t)r1, (SELECT@e:=0)e, (SELECT@prevT:=NULL)prevT\n            )r2\n        )r3\n    `,\n    values: [],\n});\n\nnode.status({text: `Done`});\n\nmsg.payload = resDb.payload[0];\n\nmsg.dailyStats = {\n\tconsumption: msg.payload.kwh,\n};\n\nreturn msg;\n",
  2261.         "outputs": 1,
  2262.         "noerr": 0,
  2263.         "initialize": "",
  2264.         "finalize": "",
  2265.         "libs": [],
  2266.         "x": 628.6666259765625,
  2267.         "y": 4478.933408021927,
  2268.         "wires": [
  2269.             [
  2270.                 "c7903419f60f94d9",
  2271.                 "53758c5d3182e479",
  2272.                 "ce47cc5f7bbcaeed"
  2273.             ]
  2274.         ]
  2275.     },
  2276.     {
  2277.         "id": "53758c5d3182e479",
  2278.         "type": "function",
  2279.         "z": "303c9862.ddfff8",
  2280.         "name": "",
  2281.         "func": "const msgs = [null, null];\n\nmsgs[msg.todayOffset] = {\n    payload: Math.round(msg.payload.kwh * 10) / 10,\n}\n\nreturn msgs;",
  2282.         "outputs": 2,
  2283.         "noerr": 0,
  2284.         "initialize": "",
  2285.         "finalize": "",
  2286.         "libs": [],
  2287.         "x": 984.6666412353516,
  2288.         "y": 4501.7334768772125,
  2289.         "wires": [
  2290.             [
  2291.                 "51ab74349f8c4265"
  2292.             ],
  2293.             [
  2294.                 "9454cd1b9fc64d0a"
  2295.             ]
  2296.         ]
  2297.     },
  2298.     {
  2299.         "id": "c7903419f60f94d9",
  2300.         "type": "debug",
  2301.         "z": "303c9862.ddfff8",
  2302.         "name": "",
  2303.         "active": false,
  2304.         "tosidebar": true,
  2305.         "console": false,
  2306.         "tostatus": false,
  2307.         "complete": "false",
  2308.         "statusVal": "",
  2309.         "statusType": "auto",
  2310.         "x": 831.6666488647461,
  2311.         "y": 4451.9332954883575,
  2312.         "wires": []
  2313.     },
  2314.     {
  2315.         "id": "9454cd1b9fc64d0a",
  2316.         "type": "ui_text",
  2317.         "z": "303c9862.ddfff8",
  2318.         "group": "802549dc0ec48149",
  2319.         "order": 4,
  2320.         "width": 0,
  2321.         "height": 0,
  2322.         "name": "",
  2323.         "label": "Consumed Yesterday",
  2324.         "format": "{{msg.payload}}kWh",
  2325.         "layout": "row-spread",
  2326.         "x": 1208.666633605957,
  2327.         "y": 4548.933429002762,
  2328.         "wires": []
  2329.     },
  2330.     {
  2331.         "id": "9b497417b6425c53",
  2332.         "type": "ui_chart",
  2333.         "z": "303c9862.ddfff8",
  2334.         "name": "PV Power",
  2335.         "group": "819686be.5e6a58",
  2336.         "order": 1,
  2337.         "width": 0,
  2338.         "height": 0,
  2339.         "label": "PV Power: {{msg.payload}}W",
  2340.         "chartType": "line",
  2341.         "legend": "false",
  2342.         "xformat": "HH:mm:ss",
  2343.         "interpolate": "linear",
  2344.         "nodata": "",
  2345.         "dot": false,
  2346.         "ymin": "0",
  2347.         "ymax": "12600",
  2348.         "removeOlder": "5",
  2349.         "removeOlderPoints": "",
  2350.         "removeOlderUnit": "60",
  2351.         "cutout": 0,
  2352.         "useOneColor": false,
  2353.         "useUTC": false,
  2354.         "colors": [
  2355.             "#fff700",
  2356.             "#aec7e8",
  2357.             "#ff7f0e",
  2358.             "#2ca02c",
  2359.             "#98df8a",
  2360.             "#d62728",
  2361.             "#ff9896",
  2362.             "#9467bd",
  2363.             "#c5b0d5"
  2364.         ],
  2365.         "outputs": 1,
  2366.         "useDifferentColor": false,
  2367.         "className": "",
  2368.         "x": 1381.6666564941406,
  2369.         "y": 2257.933335542679,
  2370.         "wires": [
  2371.             []
  2372.         ]
  2373.     },
  2374.     {
  2375.         "id": "71d63e751b4c8b6c",
  2376.         "type": "ui_chart",
  2377.         "z": "303c9862.ddfff8",
  2378.         "name": "Battery",
  2379.         "group": "819686be.5e6a58",
  2380.         "order": 5,
  2381.         "width": 0,
  2382.         "height": 0,
  2383.         "label": "Battery: {{msg.batteryMode}} {{msg.payload}}W",
  2384.         "chartType": "line",
  2385.         "legend": "false",
  2386.         "xformat": "HH:mm:ss",
  2387.         "interpolate": "step",
  2388.         "nodata": "",
  2389.         "dot": false,
  2390.         "ymin": "-8000",
  2391.         "ymax": "8000",
  2392.         "removeOlder": "5",
  2393.         "removeOlderPoints": "1000",
  2394.         "removeOlderUnit": "60",
  2395.         "cutout": 0,
  2396.         "useOneColor": false,
  2397.         "useUTC": false,
  2398.         "colors": [
  2399.             "#000000",
  2400.             "#bd0000",
  2401.             "#32bd00",
  2402.             "#bababa",
  2403.             "#98df8a",
  2404.             "#d62728",
  2405.             "#ff9896",
  2406.             "#9467bd",
  2407.             "#c5b0d5"
  2408.         ],
  2409.         "outputs": 1,
  2410.         "x": 1535.0666542053223,
  2411.         "y": 3893.7332870960236,
  2412.         "wires": [
  2413.             []
  2414.         ]
  2415.     },
  2416.     {
  2417.         "id": "4773c89f3226d39a",
  2418.         "type": "inject",
  2419.         "z": "303c9862.ddfff8",
  2420.         "name": "Charging",
  2421.         "props": [
  2422.             {
  2423.                 "p": "payload"
  2424.             },
  2425.             {
  2426.                 "p": "topic",
  2427.                 "vt": "str"
  2428.             }
  2429.         ],
  2430.         "repeat": "",
  2431.         "crontab": "",
  2432.         "once": true,
  2433.         "onceDelay": 0.1,
  2434.         "topic": "",
  2435.         "payload": "100",
  2436.         "payloadType": "num",
  2437.         "x": 891.0666465759277,
  2438.         "y": 3881.7332870960236,
  2439.         "wires": [
  2440.             [
  2441.                 "e74acd960ab80b51"
  2442.             ]
  2443.         ]
  2444.     },
  2445.     {
  2446.         "id": "3d982f3496db6194",
  2447.         "type": "function",
  2448.         "z": "303c9862.ddfff8",
  2449.         "name": "",
  2450.         "func": "function sleep(ms) {\n    return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nnode.send({\n    topic: '',\n    payload: [],\n});\n\nfor (const mode of ['Zero', 'Discharge', 'Charge', 'Idle']) {\n    await sleep(100);\n    node.send({\n        topic: mode,\n        payload: 0,\n    });\n}\n",
  2451.         "outputs": 1,
  2452.         "noerr": 0,
  2453.         "initialize": "",
  2454.         "finalize": "",
  2455.         "libs": [],
  2456.         "x": 1281.066650390625,
  2457.         "y": 3941.7332870960236,
  2458.         "wires": [
  2459.             [
  2460.                 "71d63e751b4c8b6c"
  2461.             ]
  2462.         ]
  2463.     },
  2464.     {
  2465.         "id": "e74acd960ab80b51",
  2466.         "type": "function",
  2467.         "z": "303c9862.ddfff8",
  2468.         "name": "rand",
  2469.         "func": "if (msg.payload > 0) {\n    msg.payload = {\"voltage\":400, \"current\":Math.random()*10, \"base_state_label\" : \"Charge\"};\n}else if (msg.payload < 0) {\n    msg.payload = { \"voltage\": 400, \"current\": -Math.random() * 10, \"base_state_label\": \"Discharge\"};\n}else {\n    msg.payload = {\"voltage\":400, \"current\":0, \"base_state_label\" : \"Idle\"};\n}\n\nreturn msg;",
  2470.         "outputs": 1,
  2471.         "noerr": 0,
  2472.         "initialize": "",
  2473.         "finalize": "",
  2474.         "libs": [],
  2475.         "x": 1039.666648864746,
  2476.         "y": 3894.7332870960236,
  2477.         "wires": [
  2478.             [
  2479.                 "403733106671b049"
  2480.             ]
  2481.         ]
  2482.     },
  2483.     {
  2484.         "id": "0f89590bb278d274",
  2485.         "type": "inject",
  2486.         "z": "303c9862.ddfff8",
  2487.         "name": "Discharging",
  2488.         "props": [
  2489.             {
  2490.                 "p": "payload"
  2491.             },
  2492.             {
  2493.                 "p": "topic",
  2494.                 "vt": "str"
  2495.             }
  2496.         ],
  2497.         "repeat": "",
  2498.         "crontab": "",
  2499.         "once": true,
  2500.         "onceDelay": 0.1,
  2501.         "topic": "",
  2502.         "payload": "-100",
  2503.         "payloadType": "num",
  2504.         "x": 896.666633605957,
  2505.         "y": 3919.733233690262,
  2506.         "wires": [
  2507.             [
  2508.                 "e74acd960ab80b51"
  2509.             ]
  2510.         ]
  2511.     },
  2512.     {
  2513.         "id": "8cb4808ea8445ed5",
  2514.         "type": "inject",
  2515.         "z": "303c9862.ddfff8",
  2516.         "name": "Idle",
  2517.         "props": [
  2518.             {
  2519.                 "p": "payload"
  2520.             },
  2521.             {
  2522.                 "p": "topic",
  2523.                 "vt": "str"
  2524.             }
  2525.         ],
  2526.         "repeat": "",
  2527.         "crontab": "",
  2528.         "once": true,
  2529.         "onceDelay": 0.1,
  2530.         "topic": "",
  2531.         "payload": "0",
  2532.         "payloadType": "num",
  2533.         "x": 876.666633605957,
  2534.         "y": 3957.733233690262,
  2535.         "wires": [
  2536.             [
  2537.                 "e74acd960ab80b51"
  2538.             ]
  2539.         ]
  2540.     },
  2541.     {
  2542.         "id": "189adeb07b8246d6",
  2543.         "type": "function",
  2544.         "z": "303c9862.ddfff8",
  2545.         "name": "multiplex",
  2546.         "func": "const modes = ['Discharge', 'Charge', 'Idle'].filter((v) => v != msg.batteryMode);\n//node.warn(modes);\n\nsetTimeout(() => {\n    node.send({\n        topic: 'Zero',\n        payload: 0,\n    });\n}, 1);\n\nfor (const mode of modes) {\n    setTimeout(() => {\n        node.send({\n            topic: mode,\n            payload: 0,\n        });\n    }, 1);\n}\n\nsetTimeout(() => {\n    node.send(msg);\n}, 1);\n",
  2547.         "outputs": 1,
  2548.         "noerr": 0,
  2549.         "initialize": "",
  2550.         "finalize": "",
  2551.         "libs": [],
  2552.         "x": 1386.6666564941406,
  2553.         "y": 3869.7332870960236,
  2554.         "wires": [
  2555.             [
  2556.                 "71d63e751b4c8b6c"
  2557.             ]
  2558.         ]
  2559.     },
  2560.     {
  2561.         "id": "f9b0870b92c91650",
  2562.         "type": "inject",
  2563.         "z": "303c9862.ddfff8",
  2564.         "name": "Clear",
  2565.         "props": [
  2566.             {
  2567.                 "p": "payload"
  2568.             },
  2569.             {
  2570.                 "p": "topic",
  2571.                 "vt": "str"
  2572.             }
  2573.         ],
  2574.         "repeat": "",
  2575.         "crontab": "",
  2576.         "once": true,
  2577.         "onceDelay": 0.1,
  2578.         "topic": "",
  2579.         "payload": "[]",
  2580.         "payloadType": "json",
  2581.         "x": 1153.666648864746,
  2582.         "y": 3943.733289003372,
  2583.         "wires": [
  2584.             [
  2585.                 "3d982f3496db6194"
  2586.             ]
  2587.         ]
  2588.     },
  2589.     {
  2590.         "id": "f794bbd0e4d0db3d",
  2591.         "type": "ui_text",
  2592.         "z": "303c9862.ddfff8",
  2593.         "group": "802549dc0ec48149",
  2594.         "order": 5,
  2595.         "width": 0,
  2596.         "height": 0,
  2597.         "name": "",
  2598.         "label": "Import/Export Today",
  2599.         "format": "{{msg.payload.import}}/{{msg.payload.export}}kWh",
  2600.         "layout": "row-spread",
  2601.         "x": 1204.666648864746,
  2602.         "y": 4717.733233690262,
  2603.         "wires": []
  2604.     },
  2605.     {
  2606.         "id": "961ebfcec704e4ed",
  2607.         "type": "inject",
  2608.         "z": "303c9862.ddfff8",
  2609.         "name": "",
  2610.         "props": [
  2611.             {
  2612.                 "p": "payload"
  2613.             },
  2614.             {
  2615.                 "p": "todayOffset",
  2616.                 "v": "0",
  2617.                 "vt": "str"
  2618.             }
  2619.         ],
  2620.         "repeat": "600",
  2621.         "crontab": "",
  2622.         "once": true,
  2623.         "onceDelay": 0.1,
  2624.         "topic": "",
  2625.         "payload": "",
  2626.         "payloadType": "date",
  2627.         "x": 472.66663360595703,
  2628.         "y": 4679.932781457901,
  2629.         "wires": [
  2630.             [
  2631.                 "df2ff145fbb01179"
  2632.             ]
  2633.         ]
  2634.     },
  2635.     {
  2636.         "id": "21155122bda9ae7b",
  2637.         "type": "inject",
  2638.         "z": "303c9862.ddfff8",
  2639.         "name": "",
  2640.         "props": [
  2641.             {
  2642.                 "p": "payload"
  2643.             },
  2644.             {
  2645.                 "p": "todayOffset",
  2646.                 "v": "1",
  2647.                 "vt": "str"
  2648.             }
  2649.         ],
  2650.         "repeat": "600",
  2651.         "crontab": "",
  2652.         "once": true,
  2653.         "onceDelay": 0.1,
  2654.         "topic": "",
  2655.         "payload": "",
  2656.         "payloadType": "date",
  2657.         "x": 469.66663360595703,
  2658.         "y": 4734.132731676102,
  2659.         "wires": [
  2660.             [
  2661.                 "df2ff145fbb01179"
  2662.             ]
  2663.         ]
  2664.     },
  2665.     {
  2666.         "id": "df2ff145fbb01179",
  2667.         "type": "function",
  2668.         "z": "303c9862.ddfff8",
  2669.         "name": "",
  2670.         "func": "const af = global.get('actionflows');\n\nnode.status({text: `Start`});\nconst resMsgExport = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT MAX(grid_export_kwh) AS kwh FROM (\n            SELECT time, total_ws/1000/3600 AS grid_export_kwh FROM (\n            \tSELECT time, @curr:=power_value*(IF(@prevT IS NULL, 0, time-@prevT)) as ws, @e:=@e+@curr AS total_ws, @prevT:=time FROM (\n            \t\tSELECT\n            \t\tUNIX_TIMESTAMP(log_date) AS \"time\",\n            \t\tIF(grid_meter_active_power1 > 0, grid_meter_active_power1, 0) + IF(grid_meter_active_power2 > 0, grid_meter_active_power2, 0) + IF(grid_meter_active_power3 > 0, grid_meter_active_power3, 0) AS power_value\n            \t\tFROM pv_inverter_record\n            \t\tWHERE\n            \t\tlog_date BETWEEN DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) AND DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) + INTERVAL 1 DAY\n            \t\tORDER BY log_date\n            \t)r1, (SELECT@e:=0)e, (SELECT@prevT:=NULL)prevT\n            )r2\n        )r3\n    `,\n    values: [],\n});\nnode.status({text: `${JSON.stringify({'export': resMsgExport.payload[0]})}`});\n//node.warn(resMsgExport);\n\nconst resMsgImport = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT MAX(grid_export_kwh) AS kwh FROM (\n            SELECT time, total_ws/1000/3600 AS grid_export_kwh FROM (\n            \tSELECT time, @curr:=power_value*(IF(@prevT IS NULL, 0, time-@prevT)) as ws, @e:=@e+@curr AS total_ws, @prevT:=time FROM (\n            \t\tSELECT\n            \t\tUNIX_TIMESTAMP(log_date) AS \"time\",\n            \t\t-1*(IF(grid_meter_active_power1 < 0, grid_meter_active_power1, 0) + IF(grid_meter_active_power2 < 0, grid_meter_active_power2, 0) + IF(grid_meter_active_power3 < 0, grid_meter_active_power3, 0)) AS power_value\n            \t\tFROM pv_inverter_record\n            \t\tWHERE\n            \t\tlog_date BETWEEN DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) AND DATE(NOW() - INTERVAL ${msg.todayOffset} DAY) + INTERVAL 1 DAY\n            \t\tORDER BY log_date\n            \t)r1, (SELECT@e:=0)e, (SELECT@prevT:=NULL)prevT\n            )r2\n        )r3\n    `,\n    values: [],\n});\nnode.status({text: `${JSON.stringify({'import': resMsgImport.payload[0]})}`});\n//node.warn(resMsgImport);\n\nmsg.payload = {\n    export: Math.round(resMsgExport.payload[0].kwh*100)/100,\n    import: Math.round(resMsgImport.payload[0].kwh*100)/100,\n}\n\nnode.status({text: `Done`});\n\nmsg.dailyStats = {\n    imported: msg.payload.import,\n    exported: msg.payload.export,\n};\n\nreturn msg;\n",
  2671.         "outputs": 1,
  2672.         "noerr": 0,
  2673.         "initialize": "",
  2674.         "finalize": "",
  2675.         "libs": [],
  2676.         "x": 625.6666259765625,
  2677.         "y": 4696.932760477066,
  2678.         "wires": [
  2679.             [
  2680.                 "2d6ede508a243d40",
  2681.                 "19230393a6fe7743",
  2682.                 "ce47cc5f7bbcaeed"
  2683.             ]
  2684.         ]
  2685.     },
  2686.     {
  2687.         "id": "2d6ede508a243d40",
  2688.         "type": "function",
  2689.         "z": "303c9862.ddfff8",
  2690.         "name": "",
  2691.         "func": "const msgs = [null, null];\n\nmsgs[msg.todayOffset] = {\n    payload: msg.payload,\n}\n\nreturn msgs;",
  2692.         "outputs": 2,
  2693.         "noerr": 0,
  2694.         "initialize": "",
  2695.         "finalize": "",
  2696.         "libs": [],
  2697.         "x": 981.6666412353516,
  2698.         "y": 4719.732829332352,
  2699.         "wires": [
  2700.             [
  2701.                 "f794bbd0e4d0db3d"
  2702.             ],
  2703.             [
  2704.                 "571a46d3d6a3d41e"
  2705.             ]
  2706.         ]
  2707.     },
  2708.     {
  2709.         "id": "19230393a6fe7743",
  2710.         "type": "debug",
  2711.         "z": "303c9862.ddfff8",
  2712.         "name": "",
  2713.         "active": false,
  2714.         "tosidebar": true,
  2715.         "console": false,
  2716.         "tostatus": false,
  2717.         "complete": "false",
  2718.         "statusVal": "",
  2719.         "statusType": "auto",
  2720.         "x": 851.6666450500488,
  2721.         "y": 4676.932908296585,
  2722.         "wires": []
  2723.     },
  2724.     {
  2725.         "id": "571a46d3d6a3d41e",
  2726.         "type": "ui_text",
  2727.         "z": "303c9862.ddfff8",
  2728.         "group": "802549dc0ec48149",
  2729.         "order": 6,
  2730.         "width": 0,
  2731.         "height": 0,
  2732.         "name": "",
  2733.         "label": "Import/Export Yesterday",
  2734.         "format": "{{msg.payload.import}}/{{msg.payload.export}}kWh",
  2735.         "layout": "row-spread",
  2736.         "x": 1215.666633605957,
  2737.         "y": 4766.932781457901,
  2738.         "wires": []
  2739.     },
  2740.     {
  2741.         "id": "24aa274ef9f69f21",
  2742.         "type": "function",
  2743.         "z": "303c9862.ddfff8",
  2744.         "name": "",
  2745.         "func": "msg.payload.grid_direction = msg.payload.grid_active_power > 0 ? \"Exporting\" : \"Importing\";\nreturn msg;",
  2746.         "outputs": 1,
  2747.         "noerr": 0,
  2748.         "initialize": "",
  2749.         "finalize": "",
  2750.         "libs": [],
  2751.         "x": 1221.6666564941406,
  2752.         "y": 1736.3333532810211,
  2753.         "wires": [
  2754.             [
  2755.                 "83d4257e.51d728"
  2756.             ]
  2757.         ]
  2758.     },
  2759.     {
  2760.         "id": "1c6d03dba19e8225",
  2761.         "type": "ui_text",
  2762.         "z": "303c9862.ddfff8",
  2763.         "group": "9a78c351d1914ac8",
  2764.         "order": 2,
  2765.         "width": 0,
  2766.         "height": 0,
  2767.         "name": "",
  2768.         "label": "Work Mode",
  2769.         "format": "{{msg.payload.work_mode_label}}",
  2770.         "layout": "row-spread",
  2771.         "x": 1256.666648864746,
  2772.         "y": 1513.1333992481232,
  2773.         "wires": []
  2774.     },
  2775.     {
  2776.         "id": "e0c76b9c8b0a812d",
  2777.         "type": "ui_text",
  2778.         "z": "303c9862.ddfff8",
  2779.         "group": "9a78c351d1914ac8",
  2780.         "order": 3,
  2781.         "width": 0,
  2782.         "height": 0,
  2783.         "name": "",
  2784.         "label": "Grid Mode",
  2785.         "format": "{{msg.payload.grid_mode_label}}",
  2786.         "layout": "row-spread",
  2787.         "x": 1254.666633605957,
  2788.         "y": 1552.1333801746368,
  2789.         "wires": []
  2790.     },
  2791.     {
  2792.         "id": "5b12492d69e13db2",
  2793.         "type": "pub",
  2794.         "z": "303c9862.ddfff8",
  2795.         "name": "",
  2796.         "topic": "pv.inverter.record",
  2797.         "message": "",
  2798.         "x": 1060.666648864746,
  2799.         "y": 1534.3333494663239,
  2800.         "wires": []
  2801.     },
  2802.     {
  2803.         "id": "e19475402f3c5f96",
  2804.         "type": "function",
  2805.         "z": "303c9862.ddfff8",
  2806.         "name": "merge history+forecast",
  2807.         "func": "msg.stats2 = msg.payload;\n\nmsg.payload = msg.stats1;\nmsg.payload[0].data.push(msg.stats2[0].data[0]);\nreturn msg;",
  2808.         "outputs": 1,
  2809.         "noerr": 0,
  2810.         "initialize": "",
  2811.         "finalize": "",
  2812.         "libs": [],
  2813.         "x": 1988.0667114257812,
  2814.         "y": 3250.9334251880646,
  2815.         "wires": [
  2816.             [
  2817.                 "8ce17d64b220885e"
  2818.             ]
  2819.         ]
  2820.     },
  2821.     {
  2822.         "id": "dba82e589e0e36eb",
  2823.         "type": "inject",
  2824.         "z": "303c9862.ddfff8",
  2825.         "name": "",
  2826.         "props": [
  2827.             {
  2828.                 "p": "payload"
  2829.             },
  2830.             {
  2831.                 "p": "todayOffset",
  2832.                 "v": "0",
  2833.                 "vt": "str"
  2834.             }
  2835.         ],
  2836.         "repeat": "3600",
  2837.         "crontab": "",
  2838.         "once": true,
  2839.         "onceDelay": 0.1,
  2840.         "topic": "",
  2841.         "payload": "",
  2842.         "payloadType": "date",
  2843.         "x": 467.66670989990234,
  2844.         "y": 4860.732670783997,
  2845.         "wires": [
  2846.             [
  2847.                 "98b35784bd592816"
  2848.             ]
  2849.         ]
  2850.     },
  2851.     {
  2852.         "id": "98b35784bd592816",
  2853.         "type": "function",
  2854.         "z": "303c9862.ddfff8",
  2855.         "name": "",
  2856.         "func": "const af = global.get('actionflows');\n\nconst BILLING_PERIOD_DATE_FROM = flow.get('billing_period_date_from');\n\nconst resMsgGridStats = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT SUM(imported) AS imported_kwh, SUM(exported) AS exported_kwh\n        FROM pv_daily_stats\n        WHERE log_date >= IF(STR_TO_DATE(CONCAT('${BILLING_PERIOD_DATE_FROM}', YEAR(NOW())),'%d.%m.%Y') < NOW(), STR_TO_DATE(CONCAT('${BILLING_PERIOD_DATE_FROM}', YEAR(NOW())),'%d.%m.%Y'), STR_TO_DATE(CONCAT('${BILLING_PERIOD_DATE_FROM}', YEAR(NOW())-1),'%d.%m.%Y'))\n    `,\n    values: [],\n});\n//node.warn(resMsgGridStats);\n\nmsg.payload = {\n    import: Math.round(resMsgGridStats.payload[0].imported_kwh),\n    export: Math.round(resMsgGridStats.payload[0].exported_kwh),\n}\n\nflow.set('billing_period_imported_kwh', msg.payload.import);\nflow.set('billing_period_exported_kwh', msg.payload.export);\n\nreturn msg;\n",
  2857.         "outputs": 1,
  2858.         "noerr": 0,
  2859.         "initialize": "",
  2860.         "finalize": "",
  2861.         "libs": [],
  2862.         "x": 656.6667175292969,
  2863.         "y": 4860.732739448547,
  2864.         "wires": [
  2865.             [
  2866.                 "6303ff5d796f71a2",
  2867.                 "904a6ce028e46aca"
  2868.             ]
  2869.         ]
  2870.     },
  2871.     {
  2872.         "id": "6303ff5d796f71a2",
  2873.         "type": "ui_text",
  2874.         "z": "303c9862.ddfff8",
  2875.         "group": "802549dc0ec48149",
  2876.         "order": 8,
  2877.         "width": 0,
  2878.         "height": 0,
  2879.         "name": "",
  2880.         "label": "Import/Export Billing Period",
  2881.         "format": "{{msg.payload.import}}/{{msg.payload.export}}kWh",
  2882.         "layout": "row-spread",
  2883.         "className": "",
  2884.         "x": 918.6667175292969,
  2885.         "y": 4869.732739448547,
  2886.         "wires": []
  2887.     },
  2888.     {
  2889.         "id": "6e138e18de8b9465",
  2890.         "type": "function",
  2891.         "z": "303c9862.ddfff8",
  2892.         "name": "Battery record",
  2893.         "func": "const lastBatteryRecord = flow.get('batteryRecord');\n\nif (msg.payload) {\n    let recordType;\n\n    let batteryRecord;\n\n    if ('battery_vbattery1' in msg.payload) {\n        // je to zaznam z invertoru\n        recordType = 'inverter';\n        batteryRecord = {\n            voltage: msg.payload.battery_vbattery1,\n            current: -msg.payload.battery_ibattery1,\n            base_state_label: msg.payload.battery_mode_label,\n        };\n    } else if ('base_state_label' in msg.payload) {\n        // je to zaznam z pylon bms\n        recordType = 'bms';\n        batteryRecord = msg.payload;\n        if (batteryRecord.base_state_label == 'Dischg') {\n            batteryRecord.base_state_label = 'Discharge';\n        }\n    } else if ('bmsDataTimedout' in msg) {\n        recordType = undefined;\n        batteryRecord = {\n            voltage: undefined,\n            current: undefined,\n            base_state_label: undefined,\n        };\n    }else {\n        return;\n    }\n\n    const batteryPower = Math.round(batteryRecord.voltage * batteryRecord.current);\n    const batteryMode = batteryRecord.base_state_label;\n\n    const batteryChangeSpeedText = `${batteryPower > 0 ? '+' : ''}${Math.round(100 * batteryPower / 14200 / 60 * 100) / 100}%/m`;\n\n    batteryRecord.recordType = recordType;\n    batteryRecord.batteryPower = batteryPower;\n    batteryRecord.batteryMode = batteryMode;\n    batteryRecord.batteryChangeSpeedText = batteryChangeSpeedText;\n\n    // ulozit zaznam jen pokud tam predchozi neni, nebo je to z bms, ktera prebije zaznam z invertoru\n    if (!lastBatteryRecord || lastBatteryRecord.recordType == undefined || recordType == 'bms' || recordType === undefined || lastBatteryRecord.recordType == 'inverter') {\n        flow.set('batteryRecord', batteryRecord);\n    }else {\n        return;\n    }\n\n    msg.payload = batteryRecord;\n}else {\n    msg.payload = {};\n}\n\nreturn msg;\n",
  2894.         "outputs": 1,
  2895.         "noerr": 0,
  2896.         "initialize": "",
  2897.         "finalize": "",
  2898.         "libs": [],
  2899.         "x": 1191.666648864746,
  2900.         "y": 3803.1334335803986,
  2901.         "wires": [
  2902.             [
  2903.                 "403733106671b049",
  2904.                 "e592cce1a0844c89",
  2905.                 "47897d4254f965d1"
  2906.             ]
  2907.         ]
  2908.     },
  2909.     {
  2910.         "id": "71cfbe3f9fd15264",
  2911.         "type": "actionflows",
  2912.         "z": "303c9862.ddfff8",
  2913.         "info": "Describe your action API here.",
  2914.         "untilproptype": "num",
  2915.         "proptype": "msg",
  2916.         "name": "notify",
  2917.         "prop": "loop",
  2918.         "untilprop": 0,
  2919.         "until": "gt",
  2920.         "loop": "none",
  2921.         "scope": "global",
  2922.         "perf": false,
  2923.         "seq": false,
  2924.         "x": 1358.666633605957,
  2925.         "y": 4050.933429002762,
  2926.         "wires": [
  2927.             []
  2928.         ]
  2929.     },
  2930.     {
  2931.         "id": "e592cce1a0844c89",
  2932.         "type": "function",
  2933.         "z": "303c9862.ddfff8",
  2934.         "name": "Stav",
  2935.         "func": "const lastNotifiedBatteryMonitoringStatus = flow.get('lastNotifiedBatteryMonitoringStatus');\nconst lastBatteryRecord = flow.get('batteryRecord');\n\nlet currentBatteryMonitoringStatus = `Změna stavu monitoringu baterie. Typ ${lastBatteryRecord.recordType}`;\n\nif (lastNotifiedBatteryMonitoringStatus != currentBatteryMonitoringStatus) {\n    flow.set(`lastNotifiedBatteryMonitoringStatus`, currentBatteryMonitoringStatus);\n    \n    msg.payload = { \"title\": currentBatteryMonitoringStatus, \"text\" : \"\", \"event\" : \"speech\"}\n    node.send(msg);\n}\n",
  2936.         "outputs": 1,
  2937.         "noerr": 0,
  2938.         "initialize": "",
  2939.         "finalize": "",
  2940.         "libs": [],
  2941.         "x": 1166.6666145324707,
  2942.         "y": 4055.933435678482,
  2943.         "wires": [
  2944.             [
  2945.                 "71cfbe3f9fd15264"
  2946.             ]
  2947.         ]
  2948.     },
  2949.     {
  2950.         "id": "0d6d756a53f49305",
  2951.         "type": "inject",
  2952.         "z": "303c9862.ddfff8",
  2953.         "name": "",
  2954.         "props": [
  2955.             {
  2956.                 "p": "payload"
  2957.             },
  2958.             {
  2959.                 "p": "topic",
  2960.                 "vt": "str"
  2961.             }
  2962.         ],
  2963.         "repeat": "",
  2964.         "crontab": "",
  2965.         "once": false,
  2966.         "onceDelay": 0.1,
  2967.         "topic": "",
  2968.         "payload": "{}",
  2969.         "payloadType": "json",
  2970.         "x": 867.6666488647461,
  2971.         "y": 4052.733290910721,
  2972.         "wires": [
  2973.             [
  2974.                 "6e138e18de8b9465"
  2975.             ]
  2976.         ]
  2977.     },
  2978.     {
  2979.         "id": "47897d4254f965d1",
  2980.         "type": "debug",
  2981.         "z": "303c9862.ddfff8",
  2982.         "name": "debug 4",
  2983.         "active": false,
  2984.         "tosidebar": true,
  2985.         "console": false,
  2986.         "tostatus": false,
  2987.         "complete": "false",
  2988.         "statusVal": "",
  2989.         "statusType": "auto",
  2990.         "x": 1427.6666526794434,
  2991.         "y": 3731.333382844925,
  2992.         "wires": []
  2993.     },
  2994.     {
  2995.         "id": "abe985189bbbc2f7",
  2996.         "type": "comment",
  2997.         "z": "303c9862.ddfff8",
  2998.         "name": "Grid Mode",
  2999.         "info": "",
  3000.         "x": 274,
  3001.         "y": 506.00000762939453,
  3002.         "wires": []
  3003.     },
  3004.     {
  3005.         "id": "12633529c3bef7ba",
  3006.         "type": "ui_switch",
  3007.         "z": "303c9862.ddfff8",
  3008.         "name": "",
  3009.         "label": "Group 1 - {{msg.title}}",
  3010.         "tooltip": "",
  3011.         "group": "cf60b551c034e51c",
  3012.         "order": 0,
  3013.         "width": 0,
  3014.         "height": 0,
  3015.         "passthru": false,
  3016.         "decouple": "true",
  3017.         "topic": "topic",
  3018.         "topicType": "msg",
  3019.         "style": "",
  3020.         "onvalue": "true",
  3021.         "onvalueType": "bool",
  3022.         "onicon": "",
  3023.         "oncolor": "",
  3024.         "offvalue": "false",
  3025.         "offvalueType": "bool",
  3026.         "officon": "",
  3027.         "offcolor": "",
  3028.         "animate": false,
  3029.         "className": "",
  3030.         "x": 832.0000114440918,
  3031.         "y": 594.0000066757202,
  3032.         "wires": [
  3033.             [
  3034.                 "6a2792bcd7be5504"
  3035.             ]
  3036.         ]
  3037.     },
  3038.     {
  3039.         "id": "5d07132555535339",
  3040.         "type": "function",
  3041.         "z": "303c9862.ddfff8",
  3042.         "name": "function 2",
  3043.         "func": "const mode = flow.get(\"gridMode1\") || \"backup\";\n\nmsg.payload = mode == \"backup\";\nmsg.title = mode == \"backup\" ? \"Backup\" : \"On-Grid\";\n\nreturn msg;",
  3044.         "outputs": 1,
  3045.         "noerr": 0,
  3046.         "initialize": "",
  3047.         "finalize": "",
  3048.         "libs": [],
  3049.         "x": 660.0000114440918,
  3050.         "y": 595.0000076293945,
  3051.         "wires": [
  3052.             [
  3053.                 "12633529c3bef7ba"
  3054.             ]
  3055.         ]
  3056.     },
  3057.     {
  3058.         "id": "52ae9fae03f4bc34",
  3059.         "type": "inject",
  3060.         "z": "303c9862.ddfff8",
  3061.         "name": "",
  3062.         "props": [
  3063.             {
  3064.                 "p": "payload"
  3065.             },
  3066.             {
  3067.                 "p": "topic",
  3068.                 "vt": "str"
  3069.             }
  3070.         ],
  3071.         "repeat": "",
  3072.         "crontab": "",
  3073.         "once": true,
  3074.         "onceDelay": 0.1,
  3075.         "topic": "",
  3076.         "payload": "",
  3077.         "payloadType": "date",
  3078.         "x": 295.0000305175781,
  3079.         "y": 576.0000085830688,
  3080.         "wires": [
  3081.             [
  3082.                 "a1ae3e28769e2e30"
  3083.             ]
  3084.         ]
  3085.     },
  3086.     {
  3087.         "id": "6a2792bcd7be5504",
  3088.         "type": "function",
  3089.         "z": "303c9862.ddfff8",
  3090.         "name": "function 3",
  3091.         "func": "flow.set(\"gridMode1\", msg.payload ? \"backup\" : \"on-grid\");\n\nreturn msg;",
  3092.         "outputs": 1,
  3093.         "noerr": 0,
  3094.         "initialize": "",
  3095.         "finalize": "",
  3096.         "libs": [],
  3097.         "x": 997.0000152587891,
  3098.         "y": 594.0000085830688,
  3099.         "wires": [
  3100.             [
  3101.                 "ce83228501fc0f0d"
  3102.             ]
  3103.         ]
  3104.     },
  3105.     {
  3106.         "id": "ce83228501fc0f0d",
  3107.         "type": "pub",
  3108.         "z": "303c9862.ddfff8",
  3109.         "name": "pvInverter.gridMode.changed",
  3110.         "topic": "pvInverter.gridMode.changed",
  3111.         "message": "",
  3112.         "x": 1237.8000183105469,
  3113.         "y": 635.6000576019287,
  3114.         "wires": []
  3115.     },
  3116.     {
  3117.         "id": "e1e261af6c103750",
  3118.         "type": "sub",
  3119.         "z": "303c9862.ddfff8",
  3120.         "name": "pvInverter.gridMode.changed",
  3121.         "topic": "pvInverter.gridMode.changed",
  3122.         "x": 359.8000030517578,
  3123.         "y": 658.600058555603,
  3124.         "wires": [
  3125.             [
  3126.                 "f0ca0f93933a6266"
  3127.             ]
  3128.         ]
  3129.     },
  3130.     {
  3131.         "id": "8067ee3aaceec109",
  3132.         "type": "ui_switch",
  3133.         "z": "303c9862.ddfff8",
  3134.         "name": "",
  3135.         "label": "Group 2 - {{msg.title}}",
  3136.         "tooltip": "",
  3137.         "group": "cf60b551c034e51c",
  3138.         "order": 0,
  3139.         "width": 0,
  3140.         "height": 0,
  3141.         "passthru": false,
  3142.         "decouple": "true",
  3143.         "topic": "topic",
  3144.         "topicType": "msg",
  3145.         "style": "",
  3146.         "onvalue": "true",
  3147.         "onvalueType": "bool",
  3148.         "onicon": "",
  3149.         "oncolor": "",
  3150.         "offvalue": "false",
  3151.         "offvalueType": "bool",
  3152.         "officon": "",
  3153.         "offcolor": "",
  3154.         "animate": false,
  3155.         "className": "",
  3156.         "x": 837,
  3157.         "y": 661,
  3158.         "wires": [
  3159.             [
  3160.                 "2e3f12d075e20f69"
  3161.             ]
  3162.         ]
  3163.     },
  3164.     {
  3165.         "id": "0f816f3ad14dd816",
  3166.         "type": "function",
  3167.         "z": "303c9862.ddfff8",
  3168.         "name": "function 4",
  3169.         "func": "const mode = flow.get(\"gridMode2\") || \"backup\";\n\nmsg.payload = mode == \"backup\";\nmsg.title = mode == \"backup\" ? \"Backup\" : \"On-Grid\";\n\nreturn msg;",
  3170.         "outputs": 1,
  3171.         "noerr": 0,
  3172.         "initialize": "",
  3173.         "finalize": "",
  3174.         "libs": [],
  3175.         "x": 665,
  3176.         "y": 662.0000009536743,
  3177.         "wires": [
  3178.             [
  3179.                 "8067ee3aaceec109"
  3180.             ]
  3181.         ]
  3182.     },
  3183.     {
  3184.         "id": "2e3f12d075e20f69",
  3185.         "type": "function",
  3186.         "z": "303c9862.ddfff8",
  3187.         "name": "function 5",
  3188.         "func": "flow.set(\"gridMode2\", msg.payload ? \"backup\" : \"on-grid\");\n\nreturn msg;",
  3189.         "outputs": 1,
  3190.         "noerr": 0,
  3191.         "initialize": "",
  3192.         "finalize": "",
  3193.         "libs": [],
  3194.         "x": 1002.0000038146973,
  3195.         "y": 661.0000019073486,
  3196.         "wires": [
  3197.             [
  3198.                 "ce83228501fc0f0d"
  3199.             ]
  3200.         ]
  3201.     },
  3202.     {
  3203.         "id": "61aa086381ef6356",
  3204.         "type": "ui_switch",
  3205.         "z": "303c9862.ddfff8",
  3206.         "name": "",
  3207.         "label": "Group 3 - {{msg.title}}",
  3208.         "tooltip": "",
  3209.         "group": "cf60b551c034e51c",
  3210.         "order": 0,
  3211.         "width": 0,
  3212.         "height": 0,
  3213.         "passthru": false,
  3214.         "decouple": "true",
  3215.         "topic": "topic",
  3216.         "topicType": "msg",
  3217.         "style": "",
  3218.         "onvalue": "true",
  3219.         "onvalueType": "bool",
  3220.         "onicon": "",
  3221.         "oncolor": "",
  3222.         "offvalue": "false",
  3223.         "offvalueType": "bool",
  3224.         "officon": "",
  3225.         "offcolor": "",
  3226.         "animate": false,
  3227.         "className": "",
  3228.         "x": 844,
  3229.         "y": 718,
  3230.         "wires": [
  3231.             [
  3232.                 "bfa0fbad4541e98c"
  3233.             ]
  3234.         ]
  3235.     },
  3236.     {
  3237.         "id": "0d35bbeb61b3425d",
  3238.         "type": "function",
  3239.         "z": "303c9862.ddfff8",
  3240.         "name": "function 6",
  3241.         "func": "const mode = flow.get(\"gridMode3\") || \"backup\";\n\nmsg.payload = mode == \"backup\";\nmsg.title = mode == \"backup\" ? \"Backup\" : \"On-Grid\";\n\nreturn msg;",
  3242.         "outputs": 1,
  3243.         "noerr": 0,
  3244.         "initialize": "",
  3245.         "finalize": "",
  3246.         "libs": [],
  3247.         "x": 672,
  3248.         "y": 719.0000009536743,
  3249.         "wires": [
  3250.             [
  3251.                 "61aa086381ef6356"
  3252.             ]
  3253.         ]
  3254.     },
  3255.     {
  3256.         "id": "bfa0fbad4541e98c",
  3257.         "type": "function",
  3258.         "z": "303c9862.ddfff8",
  3259.         "name": "function 7",
  3260.         "func": "flow.set(\"gridMode3\", msg.payload ? \"backup\" : \"on-grid\");\n\nreturn msg;",
  3261.         "outputs": 1,
  3262.         "noerr": 0,
  3263.         "initialize": "",
  3264.         "finalize": "",
  3265.         "libs": [],
  3266.         "x": 1009.0000038146973,
  3267.         "y": 718.0000019073486,
  3268.         "wires": [
  3269.             [
  3270.                 "ce83228501fc0f0d"
  3271.             ]
  3272.         ]
  3273.     },
  3274.     {
  3275.         "id": "a1ae3e28769e2e30",
  3276.         "type": "function",
  3277.         "z": "303c9862.ddfff8",
  3278.         "name": "function 8",
  3279.         "func": "// defaultne je okruh 3 na backupu a okruh 1 a 2 na gridu\nflow.set(\"gridMode1\", flow.get(\"gridMode1\") || \"on-grid\");\nflow.set(\"gridMode2\", flow.get(\"gridMode2\") || \"on-grid\");\nflow.set(\"gridMode3\", flow.get(\"gridMode3\") || \"backup\");\n\nreturn msg;",
  3280.         "outputs": 1,
  3281.         "noerr": 0,
  3282.         "initialize": "",
  3283.         "finalize": "",
  3284.         "libs": [],
  3285.         "x": 479.00000762939453,
  3286.         "y": 597.0000085830688,
  3287.         "wires": [
  3288.             [
  3289.                 "f0ca0f93933a6266"
  3290.             ]
  3291.         ]
  3292.     },
  3293.     {
  3294.         "id": "dc5ecd1673d5073d",
  3295.         "type": "ui_text",
  3296.         "z": "303c9862.ddfff8",
  3297.         "group": "9a78c351d1914ac8",
  3298.         "order": 1,
  3299.         "width": 0,
  3300.         "height": 0,
  3301.         "name": "",
  3302.         "label": "Last Update Time",
  3303.         "format": "{{msg.payload.lastUpdateTime}}",
  3304.         "layout": "row-spread",
  3305.         "className": "",
  3306.         "x": 1284,
  3307.         "y": 1474,
  3308.         "wires": []
  3309.     },
  3310.     {
  3311.         "id": "3cb62769caf4a4cb",
  3312.         "type": "inject",
  3313.         "z": "303c9862.ddfff8",
  3314.         "name": "",
  3315.         "props": [
  3316.             {
  3317.                 "p": "payload"
  3318.             },
  3319.             {
  3320.                 "p": "todayOffset",
  3321.                 "v": "0",
  3322.                 "vt": "str"
  3323.             }
  3324.         ],
  3325.         "repeat": "3600",
  3326.         "crontab": "",
  3327.         "once": true,
  3328.         "onceDelay": 0.1,
  3329.         "topic": "",
  3330.         "payload": "",
  3331.         "payloadType": "date",
  3332.         "x": 468.0001449584961,
  3333.         "y": 4797.999757766724,
  3334.         "wires": [
  3335.             [
  3336.                 "4a267efb39b5bc07"
  3337.             ]
  3338.         ]
  3339.     },
  3340.     {
  3341.         "id": "4a267efb39b5bc07",
  3342.         "type": "function",
  3343.         "z": "303c9862.ddfff8",
  3344.         "name": "",
  3345.         "func": "const af = global.get('actionflows');\n\nconst resMsgGridStats = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT SUM(imported) AS imported_kwh, SUM(exported) AS exported_kwh FROM pv_daily_stats\n    `,\n    values: [],\n});\n//node.warn(resMsgGridStats);\n\nmsg.payload = {\n    import: Math.round(resMsgGridStats.payload[0].imported_kwh),\n    export: Math.round(resMsgGridStats.payload[0].exported_kwh),\n}\n\nreturn msg;\n",
  3346.         "outputs": 1,
  3347.         "noerr": 0,
  3348.         "initialize": "",
  3349.         "finalize": "",
  3350.         "libs": [],
  3351.         "x": 657.0001525878906,
  3352.         "y": 4797.999826431274,
  3353.         "wires": [
  3354.             [
  3355.                 "730fbcb2a8b18234"
  3356.             ]
  3357.         ]
  3358.     },
  3359.     {
  3360.         "id": "730fbcb2a8b18234",
  3361.         "type": "ui_text",
  3362.         "z": "303c9862.ddfff8",
  3363.         "group": "802549dc0ec48149",
  3364.         "order": 7,
  3365.         "width": 0,
  3366.         "height": 0,
  3367.         "name": "",
  3368.         "label": "Import/Export Total",
  3369.         "format": "{{msg.payload.import}}/{{msg.payload.export}}kWh",
  3370.         "layout": "row-spread",
  3371.         "x": 889.0001525878906,
  3372.         "y": 4806.999826431274,
  3373.         "wires": []
  3374.     },
  3375.     {
  3376.         "id": "4b25455cea161d4f",
  3377.         "type": "inject",
  3378.         "z": "303c9862.ddfff8",
  3379.         "name": "",
  3380.         "props": [
  3381.             {
  3382.                 "p": "payload"
  3383.             },
  3384.             {
  3385.                 "p": "topic",
  3386.                 "vt": "str"
  3387.             }
  3388.         ],
  3389.         "repeat": "",
  3390.         "crontab": "",
  3391.         "once": true,
  3392.         "onceDelay": 0.1,
  3393.         "topic": "",
  3394.         "payload": "",
  3395.         "payloadType": "date",
  3396.         "x": 594.0000228881836,
  3397.         "y": 90.00000190734863,
  3398.         "wires": [
  3399.             [
  3400.                 "00e399cab5f30c62"
  3401.             ]
  3402.         ]
  3403.     },
  3404.     {
  3405.         "id": "00e399cab5f30c62",
  3406.         "type": "function",
  3407.         "z": "303c9862.ddfff8",
  3408.         "name": "Billing Period",
  3409.         "func": "flow.set('billing_period_date_from', '1.1.')\nreturn msg;",
  3410.         "outputs": 1,
  3411.         "noerr": 0,
  3412.         "initialize": "",
  3413.         "finalize": "",
  3414.         "libs": [],
  3415.         "x": 785.0000267028809,
  3416.         "y": 94.00000190734863,
  3417.         "wires": [
  3418.             []
  3419.         ]
  3420.     },
  3421.     {
  3422.         "id": "33bd738d8d0650a4",
  3423.         "type": "comment",
  3424.         "z": "303c9862.ddfff8",
  3425.         "name": "Billing Period",
  3426.         "info": "",
  3427.         "x": 398.8000259399414,
  3428.         "y": 81,
  3429.         "wires": []
  3430.     },
  3431.     {
  3432.         "id": "bc31c91c29d3fac8",
  3433.         "type": "ui_text",
  3434.         "z": "303c9862.ddfff8",
  3435.         "group": "d9bbfe4e.3d039",
  3436.         "order": 2,
  3437.         "width": 0,
  3438.         "height": 0,
  3439.         "name": "",
  3440.         "label": "Rest Power Today",
  3441.         "format": "{{msg.payload.rest}}kWh ({{msg.payload.rest1}}+{{msg.payload.rest2}})",
  3442.         "layout": "row-spread",
  3443.         "className": "",
  3444.         "x": 1189,
  3445.         "y": 1027,
  3446.         "wires": []
  3447.     },
  3448.     {
  3449.         "id": "3c0f0981be58e691",
  3450.         "type": "sun-position",
  3451.         "z": "303c9862.ddfff8",
  3452.         "name": "",
  3453.         "positionConfig": "347876e0a6d5c1b0",
  3454.         "rules": [],
  3455.         "onlyOnChange": "true",
  3456.         "topic": "",
  3457.         "outputs": 1,
  3458.         "start": "",
  3459.         "startType": "none",
  3460.         "startOffset": 0,
  3461.         "startOffsetType": "none",
  3462.         "startOffsetMultiplier": 60000,
  3463.         "end": "",
  3464.         "endType": "none",
  3465.         "endOffset": 0,
  3466.         "endOffsetType": "none",
  3467.         "endOffsetMultiplier": 60000,
  3468.         "x": 2008.2000350952148,
  3469.         "y": 265.19997119903564,
  3470.         "wires": [
  3471.             [
  3472.                 "befbe07cb3745cb5",
  3473.                 "c82062ebc86dc60c",
  3474.                 "5b26781b33a634d7",
  3475.                 "e162b0ea29fdf26f",
  3476.                 "889fe2593fed52ab",
  3477.                 "d91fb2ef78f1148b"
  3478.             ]
  3479.         ]
  3480.     },
  3481.     {
  3482.         "id": "4fa5989c1f3111d8",
  3483.         "type": "inject",
  3484.         "z": "303c9862.ddfff8",
  3485.         "name": "",
  3486.         "props": [
  3487.             {
  3488.                 "p": "payload"
  3489.             },
  3490.             {
  3491.                 "p": "topic",
  3492.                 "vt": "str"
  3493.             }
  3494.         ],
  3495.         "repeat": "60",
  3496.         "crontab": "",
  3497.         "once": false,
  3498.         "onceDelay": 0.1,
  3499.         "topic": "",
  3500.         "payload": "",
  3501.         "payloadType": "date",
  3502.         "x": 1847.2000350952148,
  3503.         "y": 273.19997119903564,
  3504.         "wires": [
  3505.             [
  3506.                 "3c0f0981be58e691"
  3507.             ]
  3508.         ]
  3509.     },
  3510.     {
  3511.         "id": "befbe07cb3745cb5",
  3512.         "type": "debug",
  3513.         "z": "303c9862.ddfff8",
  3514.         "name": "debug 9",
  3515.         "active": false,
  3516.         "tosidebar": true,
  3517.         "console": false,
  3518.         "tostatus": false,
  3519.         "complete": "false",
  3520.         "statusVal": "",
  3521.         "statusType": "auto",
  3522.         "x": 2209.200168609619,
  3523.         "y": 185.19998359680176,
  3524.         "wires": []
  3525.     },
  3526.     {
  3527.         "id": "2046a24e0c2fdc92",
  3528.         "type": "ui_template",
  3529.         "z": "303c9862.ddfff8",
  3530.         "group": "45ecb2bfdd21bc90",
  3531.         "name": "",
  3532.         "order": 2,
  3533.         "width": 0,
  3534.         "height": 0,
  3535.         "format": "<div class=\"sun-position\">\n    <img class=\"dum\" src=\"/img/dum.webp\">\n    <div class=\"center\">\n        <div class=\"line sunrise\" style=\"{{msg.payload.sunPosition.sunrise.azimuthTransform}}\"></div>\n        <div class=\"line sunset\" style=\"{{msg.payload.sunPosition.sunset.azimuthTransform}}\"></div>\n        <div class=\"line sun\" style=\"{{msg.payload.sunPosition.now.azimuthTransform}}\"></div>\n        <div class=\"dot\"></div>\n    </div>\n</div>\n\n\n<style>\n.sun-position {\n    height: 272px;\n    display: grid;\n    grid-template-columns: 1fr;\n    grid-template-rows: 1fr;\n    grid-template-areas: \"content\";\n    justify-items: center;\n    align-items: center;\n    overflow: hidden;\n}\n.sun-position .dum {\n    grid-area: content;\n    border: 2px solid rgb(0, 218, 0);\n    object-fit: contain;\n    width: 100%; /* or any custom size */\n    height: 100%;\n}\n.sun-position .center {\n    grid-area: content;\n    height: 1px;\n    width: 1px;\n    position: relative;\n    left: -0.5px;\n    bottom: -0.5px;\n}\n.sun-position .dot {\n    position: absolute;\n    left: -4px;\n    bottom: -4px;\n    background-color: rgb(210 92 1);\n    border-radius: 50%;\n    height: 8px;\n    width: 8px;\n}\n.sun-position .line {\n    position: absolute;\n    left: -2px;\n    bottom: -2px;\n    height: 200px;\n    width: 4px;\n    border-radius: 4px;\n    transform-origin: 2px calc(100% - 2px);\n}\n.sun-position .sun {\n    background-color: rgb(238 238 24 / 90%);\n}\n.sun-position .sunrise, .sun-position .sunset {\n    background-color: rgb(174 174 174 / 80%);\n    width: 2px;\n}\n</style>",
  3536.         "storeOutMessages": true,
  3537.         "fwdInMessages": true,
  3538.         "resendOnRefresh": true,
  3539.         "templateScope": "local",
  3540.         "className": "sun-position-widget",
  3541.         "x": 2250.2001037597656,
  3542.         "y": 681.2000370025635,
  3543.         "wires": [
  3544.             []
  3545.         ]
  3546.     },
  3547.     {
  3548.         "id": "c82062ebc86dc60c",
  3549.         "type": "function",
  3550.         "z": "303c9862.ddfff8",
  3551.         "name": "function 16",
  3552.         "func": "flow.set('sunPositionNow', msg.payload);\n\n",
  3553.         "outputs": 1,
  3554.         "noerr": 0,
  3555.         "initialize": "",
  3556.         "finalize": "",
  3557.         "libs": [],
  3558.         "x": 2211.199924468994,
  3559.         "y": 235.1999855041504,
  3560.         "wires": [
  3561.             []
  3562.         ]
  3563.     },
  3564.     {
  3565.         "id": "2defc3ce36d57aa7",
  3566.         "type": "comment",
  3567.         "z": "303c9862.ddfff8",
  3568.         "name": "sun",
  3569.         "info": "",
  3570.         "x": 1734.199951171875,
  3571.         "y": 169.1999969482422,
  3572.         "wires": []
  3573.     },
  3574.     {
  3575.         "id": "5b26781b33a634d7",
  3576.         "type": "within-time-switch",
  3577.         "z": "303c9862.ddfff8",
  3578.         "name": "",
  3579.         "nameInt": "",
  3580.         "positionConfig": "347876e0a6d5c1b0",
  3581.         "startTime": "0:00",
  3582.         "startTimeType": "entered",
  3583.         "startOffset": 0,
  3584.         "startOffsetType": "none",
  3585.         "startOffsetMultiplier": 60000,
  3586.         "endTime": "0:00",
  3587.         "endTimeType": "entered",
  3588.         "endOffset": 0,
  3589.         "endOffsetType": "none",
  3590.         "endOffsetMultiplier": 60000,
  3591.         "timeRestrictions": 0,
  3592.         "timeRestrictionsType": "none",
  3593.         "timeDays": "*",
  3594.         "timeOnlyOddDays": false,
  3595.         "timeOnlyEvenDays": false,
  3596.         "timeOnlyOddWeeks": false,
  3597.         "timeOnlyEvenWeeks": false,
  3598.         "timeMonths": "*",
  3599.         "timedatestart": "",
  3600.         "timedateend": "",
  3601.         "propertyStart": "",
  3602.         "propertyStartType": "none",
  3603.         "propertyStartCompare": "true",
  3604.         "propertyStartThreshold": "",
  3605.         "propertyStartThresholdType": "num",
  3606.         "startTimeAlt": "",
  3607.         "startTimeAltType": "entered",
  3608.         "startOffsetAlt": 0,
  3609.         "startOffsetAltType": "none",
  3610.         "startOffsetAltMultiplier": 60000,
  3611.         "propertyEnd": "",
  3612.         "propertyEndType": "none",
  3613.         "propertyEndCompare": "true",
  3614.         "propertyEndThreshold": "",
  3615.         "propertyEndThresholdType": "num",
  3616.         "endTimeAlt": "",
  3617.         "endTimeAltType": "entered",
  3618.         "endOffsetAlt": 0,
  3619.         "endOffsetAltType": "none",
  3620.         "endOffsetAltMultiplier": 60000,
  3621.         "withinTimeValue": "$getSunCalc(payload.times.sunrise.ts, true, true)",
  3622.         "withinTimeValueType": "jsonata",
  3623.         "outOfTimeValue": "false",
  3624.         "outOfTimeValueType": "msgInput",
  3625.         "tsCompare": "0",
  3626.         "x": 2265.1999015808105,
  3627.         "y": 328.1999912261963,
  3628.         "wires": [
  3629.             [
  3630.                 "e901bc4cc16319a1",
  3631.                 "0f4de5abbd27c721"
  3632.             ],
  3633.             []
  3634.         ]
  3635.     },
  3636.     {
  3637.         "id": "e901bc4cc16319a1",
  3638.         "type": "debug",
  3639.         "z": "303c9862.ddfff8",
  3640.         "name": "debug 13",
  3641.         "active": false,
  3642.         "tosidebar": true,
  3643.         "console": false,
  3644.         "tostatus": false,
  3645.         "complete": "false",
  3646.         "statusVal": "",
  3647.         "statusType": "auto",
  3648.         "x": 2455.199903488159,
  3649.         "y": 301.19996070861816,
  3650.         "wires": []
  3651.     },
  3652.     {
  3653.         "id": "e162b0ea29fdf26f",
  3654.         "type": "within-time-switch",
  3655.         "z": "303c9862.ddfff8",
  3656.         "name": "",
  3657.         "nameInt": "",
  3658.         "positionConfig": "347876e0a6d5c1b0",
  3659.         "startTime": "0:00",
  3660.         "startTimeType": "entered",
  3661.         "startOffset": 0,
  3662.         "startOffsetType": "none",
  3663.         "startOffsetMultiplier": 60000,
  3664.         "endTime": "0:00",
  3665.         "endTimeType": "entered",
  3666.         "endOffset": 0,
  3667.         "endOffsetType": "none",
  3668.         "endOffsetMultiplier": 60000,
  3669.         "timeRestrictions": 0,
  3670.         "timeRestrictionsType": "none",
  3671.         "timeDays": "*",
  3672.         "timeOnlyOddDays": false,
  3673.         "timeOnlyEvenDays": false,
  3674.         "timeOnlyOddWeeks": false,
  3675.         "timeOnlyEvenWeeks": false,
  3676.         "timeMonths": "*",
  3677.         "timedatestart": "",
  3678.         "timedateend": "",
  3679.         "propertyStart": "",
  3680.         "propertyStartType": "none",
  3681.         "propertyStartCompare": "true",
  3682.         "propertyStartThreshold": "",
  3683.         "propertyStartThresholdType": "num",
  3684.         "startTimeAlt": "",
  3685.         "startTimeAltType": "entered",
  3686.         "startOffsetAlt": 0,
  3687.         "startOffsetAltType": "none",
  3688.         "startOffsetAltMultiplier": 60000,
  3689.         "propertyEnd": "",
  3690.         "propertyEndType": "none",
  3691.         "propertyEndCompare": "true",
  3692.         "propertyEndThreshold": "",
  3693.         "propertyEndThresholdType": "num",
  3694.         "endTimeAlt": "",
  3695.         "endTimeAltType": "entered",
  3696.         "endOffsetAlt": 0,
  3697.         "endOffsetAltType": "none",
  3698.         "endOffsetAltMultiplier": 60000,
  3699.         "withinTimeValue": "$getSunCalc(payload.times.sunset.ts, true, true)",
  3700.         "withinTimeValueType": "jsonata",
  3701.         "outOfTimeValue": "false",
  3702.         "outOfTimeValueType": "msgInput",
  3703.         "tsCompare": "0",
  3704.         "x": 2267.199920654297,
  3705.         "y": 451.19997215270996,
  3706.         "wires": [
  3707.             [
  3708.                 "fe3237283aa29bb5",
  3709.                 "d959b85c0443d3cd"
  3710.             ],
  3711.             []
  3712.         ]
  3713.     },
  3714.     {
  3715.         "id": "fe3237283aa29bb5",
  3716.         "type": "debug",
  3717.         "z": "303c9862.ddfff8",
  3718.         "name": "debug 14",
  3719.         "active": false,
  3720.         "tosidebar": true,
  3721.         "console": false,
  3722.         "tostatus": false,
  3723.         "complete": "false",
  3724.         "statusVal": "",
  3725.         "statusType": "auto",
  3726.         "x": 2444.1998958587646,
  3727.         "y": 437.1999626159668,
  3728.         "wires": []
  3729.     },
  3730.     {
  3731.         "id": "0f4de5abbd27c721",
  3732.         "type": "function",
  3733.         "z": "303c9862.ddfff8",
  3734.         "name": "function 17",
  3735.         "func": "flow.set('sunPositionSunrise', msg.payload);\n",
  3736.         "outputs": 1,
  3737.         "noerr": 0,
  3738.         "initialize": "",
  3739.         "finalize": "",
  3740.         "libs": [],
  3741.         "x": 2462.199926376343,
  3742.         "y": 341.19997215270996,
  3743.         "wires": [
  3744.             []
  3745.         ]
  3746.     },
  3747.     {
  3748.         "id": "d959b85c0443d3cd",
  3749.         "type": "function",
  3750.         "z": "303c9862.ddfff8",
  3751.         "name": "function 18",
  3752.         "func": "flow.set('sunPositionSunset', msg.payload);\n",
  3753.         "outputs": 1,
  3754.         "noerr": 0,
  3755.         "initialize": "",
  3756.         "finalize": "",
  3757.         "libs": [],
  3758.         "x": 2456.199926376343,
  3759.         "y": 482.20000410079956,
  3760.         "wires": [
  3761.             []
  3762.         ]
  3763.     },
  3764.     {
  3765.         "id": "e1f413b8747a3e23",
  3766.         "type": "function",
  3767.         "z": "303c9862.ddfff8",
  3768.         "name": "function 19",
  3769.         "func": "const sunPositionNow = flow.get('sunPositionNow');\nconst sunPositionSunrise = flow.get('sunPositionSunrise');\nconst sunPositionSunset = flow.get('sunPositionSunset');\n\nsunPositionNow.azimuthTransform = `transform: rotateZ(${sunPositionNow.azimuthDegrees}deg)`;\nsunPositionSunrise.azimuthTransform = `transform: rotateZ(${sunPositionSunrise.azimuthDegrees}deg)`;\nsunPositionSunset.azimuthTransform = `transform: rotateZ(${sunPositionSunset.azimuthDegrees}deg)`;\n\n\nmsg.payload = {\n    sunPosition: {\n        now: sunPositionNow,\n        sunrise: sunPositionSunrise,\n        sunset: sunPositionSunset,\n    }\n}\nreturn msg;",
  3770.         "outputs": 1,
  3771.         "noerr": 0,
  3772.         "initialize": "",
  3773.         "finalize": "",
  3774.         "libs": [],
  3775.         "x": 2077.199981689453,
  3776.         "y": 679.2000350952148,
  3777.         "wires": [
  3778.             [
  3779.                 "2046a24e0c2fdc92"
  3780.             ]
  3781.         ]
  3782.     },
  3783.     {
  3784.         "id": "685cf8222df25623",
  3785.         "type": "inject",
  3786.         "z": "303c9862.ddfff8",
  3787.         "name": "",
  3788.         "props": [
  3789.             {
  3790.                 "p": "payload"
  3791.             },
  3792.             {
  3793.                 "p": "topic",
  3794.                 "vt": "str"
  3795.             }
  3796.         ],
  3797.         "repeat": "60",
  3798.         "crontab": "",
  3799.         "once": false,
  3800.         "onceDelay": 0.1,
  3801.         "topic": "",
  3802.         "payload": "",
  3803.         "payloadType": "date",
  3804.         "x": 1894.1999740600586,
  3805.         "y": 677.2000350952148,
  3806.         "wires": [
  3807.             [
  3808.                 "e1f413b8747a3e23"
  3809.             ]
  3810.         ]
  3811.     },
  3812.     {
  3813.         "id": "1c67febfe1ce1abd",
  3814.         "type": "function",
  3815.         "z": "303c9862.ddfff8",
  3816.         "name": "disp_pwr",
  3817.         "func": "if (msg.payload.type == \"disp_pwr\") {\n    msg.payload = msg.payload.data;\n    return msg;\n}",
  3818.         "outputs": 1,
  3819.         "noerr": 0,
  3820.         "initialize": "",
  3821.         "finalize": "",
  3822.         "libs": [],
  3823.         "x": 757.2000350952148,
  3824.         "y": 3687.200006365776,
  3825.         "wires": [
  3826.             [
  3827.                 "be5efd648227638b"
  3828.             ]
  3829.         ]
  3830.     },
  3831.     {
  3832.         "id": "c94a357adb066e7e",
  3833.         "type": "function",
  3834.         "z": "303c9862.ddfff8",
  3835.         "name": "",
  3836.         "func": "msg.recordType = flow.get('batteryRecord').recordType;\nreturn msg;",
  3837.         "outputs": 1,
  3838.         "noerr": 0,
  3839.         "initialize": "",
  3840.         "finalize": "",
  3841.         "libs": [],
  3842.         "x": 1345.2000465393066,
  3843.         "y": 1951.5998821258545,
  3844.         "wires": [
  3845.             [
  3846.                 "b3e26726cf0f4701"
  3847.             ]
  3848.         ]
  3849.     },
  3850.     {
  3851.         "id": "b3e26726cf0f4701",
  3852.         "type": "ui_text",
  3853.         "z": "303c9862.ddfff8",
  3854.         "group": "b55f633.0a700a",
  3855.         "order": 0,
  3856.         "width": 0,
  3857.         "height": 0,
  3858.         "name": "",
  3859.         "label": "Record Type",
  3860.         "format": "{{msg.recordType}}",
  3861.         "layout": "row-spread",
  3862.         "className": "",
  3863.         "x": 1496.200050354004,
  3864.         "y": 1949.600004196167,
  3865.         "wires": []
  3866.     },
  3867.     {
  3868.         "id": "d0c51b4e08b32eb2",
  3869.         "type": "function",
  3870.         "z": "303c9862.ddfff8",
  3871.         "name": "stat",
  3872.         "func": "if (msg.payload.type == \"stat\") {\n    msg.payload = msg.payload.data;\n\n    const columns = Object.keys(msg.payload).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n    msg.topic = `INSERT INTO pv_battery_stat SET log_date = NOW(), ${columns}`;\n    msg.payload = Object.values(msg.payload);\n\n    return msg;\n}",
  3873.         "outputs": 1,
  3874.         "noerr": 0,
  3875.         "initialize": "",
  3876.         "finalize": "",
  3877.         "libs": [],
  3878.         "x": 1631.199951171875,
  3879.         "y": 3588.400146484375,
  3880.         "wires": [
  3881.             [
  3882.                 "782e5b5ab3f03abb"
  3883.             ]
  3884.         ]
  3885.     },
  3886.     {
  3887.         "id": "782e5b5ab3f03abb",
  3888.         "type": "mysql",
  3889.         "z": "303c9862.ddfff8",
  3890.         "mydb": "76a2023f.8c254c",
  3891.         "name": "",
  3892.         "x": 1799.866554260254,
  3893.         "y": 3587.7336959838867,
  3894.         "wires": [
  3895.             [
  3896.                 "d7de585efcc764fc"
  3897.             ]
  3898.         ]
  3899.     },
  3900.     {
  3901.         "id": "d7de585efcc764fc",
  3902.         "type": "debug",
  3903.         "z": "303c9862.ddfff8",
  3904.         "name": "debug 15",
  3905.         "active": false,
  3906.         "tosidebar": true,
  3907.         "console": false,
  3908.         "tostatus": false,
  3909.         "complete": "false",
  3910.         "statusVal": "",
  3911.         "statusType": "auto",
  3912.         "x": 1960.2000579833984,
  3913.         "y": 3605.4001998901367,
  3914.         "wires": []
  3915.     },
  3916.     {
  3917.         "id": "8e3f2accc4d47cf1",
  3918.         "type": "function",
  3919.         "z": "303c9862.ddfff8",
  3920.         "name": "sysinfo",
  3921.         "func": "if (msg.payload.type == \"sysinfo\") {\n    msg.payload = msg.payload.data;\n\n    const columns = Object.keys(msg.payload).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n    msg.topic = `INSERT INTO pv_battery_sysinfo SET log_date = NOW(), ${columns}`;\n    msg.payload = Object.values(msg.payload);\n\n    return msg;\n}",
  3922.         "outputs": 1,
  3923.         "noerr": 0,
  3924.         "initialize": "",
  3925.         "finalize": "",
  3926.         "libs": [],
  3927.         "x": 1643.199951171875,
  3928.         "y": 3630.400146484375,
  3929.         "wires": [
  3930.             [
  3931.                 "bcc98baeb30e95b9"
  3932.             ]
  3933.         ]
  3934.     },
  3935.     {
  3936.         "id": "bcc98baeb30e95b9",
  3937.         "type": "mysql",
  3938.         "z": "303c9862.ddfff8",
  3939.         "mydb": "76a2023f.8c254c",
  3940.         "name": "",
  3941.         "x": 1799.866554260254,
  3942.         "y": 3629.7336959838867,
  3943.         "wires": [
  3944.             [
  3945.                 "d7de585efcc764fc"
  3946.             ]
  3947.         ]
  3948.     },
  3949.     {
  3950.         "id": "535ea969ba5e5c16",
  3951.         "type": "function",
  3952.         "z": "303c9862.ddfff8",
  3953.         "name": "bat",
  3954.         "func": "if (msg.payload.type == \"bat\") {\n    const dataBatteries = msg.payload.data.batteries;\n\n    msg.payload = msg.payload.data.columnValues;\n\n    delete msg.payload.batteries;\n\n    const columns = Object.keys(msg.payload).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n    node.send({\n        topic: `INSERT INTO pv_battery_bat SET log_date = NOW(), ${columns}`,\n        payload: Object.values(msg.payload),\n    });\n\n    for (const dataBattery of dataBatteries) {\n        const columns = Object.keys(dataBattery).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_bat_battery SET ${columns}`,\n            payload: Object.values(dataBattery),\n        });\n    }\n}",
  3955.         "outputs": 1,
  3956.         "noerr": 0,
  3957.         "initialize": "",
  3958.         "finalize": "",
  3959.         "libs": [],
  3960.         "x": 1637.199951171875,
  3961.         "y": 3672.400146484375,
  3962.         "wires": [
  3963.             [
  3964.                 "1fd6a0a74c46a2a6"
  3965.             ]
  3966.         ]
  3967.     },
  3968.     {
  3969.         "id": "1fd6a0a74c46a2a6",
  3970.         "type": "mysql",
  3971.         "z": "303c9862.ddfff8",
  3972.         "mydb": "76a2023f.8c254c",
  3973.         "name": "",
  3974.         "x": 1803.866554260254,
  3975.         "y": 3671.7336959838867,
  3976.         "wires": [
  3977.             [
  3978.                 "d7de585efcc764fc"
  3979.             ]
  3980.         ]
  3981.     },
  3982.     {
  3983.         "id": "dd5dd723ead7dd78",
  3984.         "type": "function",
  3985.         "z": "303c9862.ddfff8",
  3986.         "name": "soh",
  3987.         "func": "if (msg.payload.type == \"soh\") {\n    const dataBatteries = msg.payload.data.batteries;\n\n    msg.payload = msg.payload.data.columnValues;\n\n    delete msg.payload.batteries;\n\n    const columns = Object.keys(msg.payload).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n    node.send({\n        topic: `INSERT INTO pv_battery_soh SET log_date = NOW(), ${columns}`,\n        payload: Object.values(msg.payload),\n    });\n\n    for (const dataBattery of dataBatteries) {\n        const columns = Object.keys(dataBattery).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_soh_battery SET ${columns}`,\n            payload: Object.values(dataBattery),\n        });\n    }\n}",
  3988.         "outputs": 1,
  3989.         "noerr": 0,
  3990.         "initialize": "",
  3991.         "finalize": "",
  3992.         "libs": [],
  3993.         "x": 1634.199951171875,
  3994.         "y": 3714.199951171875,
  3995.         "wires": [
  3996.             [
  3997.                 "9d612173b6f1a17d"
  3998.             ]
  3999.         ]
  4000.     },
  4001.     {
  4002.         "id": "9d612173b6f1a17d",
  4003.         "type": "mysql",
  4004.         "z": "303c9862.ddfff8",
  4005.         "mydb": "76a2023f.8c254c",
  4006.         "name": "",
  4007.         "x": 1800.866554260254,
  4008.         "y": 3713.5335006713867,
  4009.         "wires": [
  4010.             [
  4011.                 "d7de585efcc764fc"
  4012.             ]
  4013.         ]
  4014.     },
  4015.     {
  4016.         "id": "8d0de0a23b20e624",
  4017.         "type": "function",
  4018.         "z": "303c9862.ddfff8",
  4019.         "name": "log",
  4020.         "func": "if (msg.payload.type == \"log\") {\n    const dataRecords = msg.payload.data;\n\n    for (const dataRecord of dataRecords) {\n        const columns = Object.keys(dataRecord).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_log SET log_date = NOW(), ${columns} ON DUPLICATE KEY UPDATE uuid=uuid`,\n            payload: Object.values(dataRecord),\n        });\n    }\n}",
  4021.         "outputs": 1,
  4022.         "noerr": 0,
  4023.         "initialize": "",
  4024.         "finalize": "",
  4025.         "libs": [],
  4026.         "x": 1638.199951171875,
  4027.         "y": 3754.199951171875,
  4028.         "wires": [
  4029.             [
  4030.                 "341ca86f121477a6"
  4031.             ]
  4032.         ]
  4033.     },
  4034.     {
  4035.         "id": "341ca86f121477a6",
  4036.         "type": "mysql",
  4037.         "z": "303c9862.ddfff8",
  4038.         "mydb": "76a2023f.8c254c",
  4039.         "name": "",
  4040.         "x": 1804.866554260254,
  4041.         "y": 3753.5335006713867,
  4042.         "wires": [
  4043.             [
  4044.                 "d7de585efcc764fc"
  4045.             ]
  4046.         ]
  4047.     },
  4048.     {
  4049.         "id": "656b1803a57a159b",
  4050.         "type": "function",
  4051.         "z": "303c9862.ddfff8",
  4052.         "name": "data event",
  4053.         "func": "if (msg.payload.type == \"data event\") {\n    const dataBatteries = msg.payload.data.batteries;\n    const dataUnits = msg.payload.data.units;\n\n    msg.payload = msg.payload.data.columnValues;\n\n    delete msg.payload.batteries;\n\n    const columns = Object.keys(msg.payload).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n    node.send({\n        topic: `INSERT INTO pv_battery_data_event SET log_date = NOW(), ${columns} ON DUPLICATE KEY UPDATE uuid=uuid`,\n        payload: Object.values(msg.payload),\n    });\n\n    for (const dataBattery of dataBatteries) {\n        const columns = Object.keys(dataBattery).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_data_event_battery SET ${columns} ON DUPLICATE KEY UPDATE parent_uuid=parent_uuid`,\n            payload: Object.values(dataBattery),\n        });\n    }\n\n    for (const dataUnit of dataUnits) {\n        const columns = Object.keys(dataUnit).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_data_event_unit SET ${columns} ON DUPLICATE KEY UPDATE parent_uuid=parent_uuid`,\n            payload: Object.values(dataUnit),\n        });\n    }\n}",
  4054.         "outputs": 1,
  4055.         "noerr": 0,
  4056.         "initialize": "",
  4057.         "finalize": "",
  4058.         "libs": [],
  4059.         "x": 1660.199951171875,
  4060.         "y": 3794.199951171875,
  4061.         "wires": [
  4062.             [
  4063.                 "a1610872c8680840"
  4064.             ]
  4065.         ]
  4066.     },
  4067.     {
  4068.         "id": "a1610872c8680840",
  4069.         "type": "mysql",
  4070.         "z": "303c9862.ddfff8",
  4071.         "mydb": "76a2023f.8c254c",
  4072.         "name": "",
  4073.         "x": 1806.866554260254,
  4074.         "y": 3793.5335006713867,
  4075.         "wires": [
  4076.             []
  4077.         ]
  4078.     },
  4079.     {
  4080.         "id": "b562397f4dc61e83",
  4081.         "type": "function",
  4082.         "z": "303c9862.ddfff8",
  4083.         "name": "data history",
  4084.         "func": "if (msg.payload.type == \"data history\") {\n    const dataBatteries = msg.payload.data.batteries;\n    const dataUnits = msg.payload.data.units;\n\n    msg.payload = msg.payload.data.columnValues;\n\n    delete msg.payload.batteries;\n\n    const columns = Object.keys(msg.payload).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n    node.send({\n        topic: `INSERT INTO pv_battery_data_history SET log_date = NOW(), ${columns} ON DUPLICATE KEY UPDATE uuid=uuid`,\n        payload: Object.values(msg.payload),\n    });\n\n    for (const dataBattery of dataBatteries) {\n        const columns = Object.keys(dataBattery).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_data_history_battery SET ${columns} ON DUPLICATE KEY UPDATE parent_uuid=parent_uuid`,\n            payload: Object.values(dataBattery),\n        });\n    }\n\n    for (const dataUnit of dataUnits) {\n        const columns = Object.keys(dataUnit).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_data_history_unit SET ${columns} ON DUPLICATE KEY UPDATE parent_uuid=parent_uuid`,\n            payload: Object.values(dataUnit),\n        });\n    }\n}",
  4085.         "outputs": 1,
  4086.         "noerr": 0,
  4087.         "initialize": "",
  4088.         "finalize": "",
  4089.         "libs": [],
  4090.         "x": 1660.199951171875,
  4091.         "y": 3828.199951171875,
  4092.         "wires": [
  4093.             [
  4094.                 "0573d01071f92415"
  4095.             ]
  4096.         ]
  4097.     },
  4098.     {
  4099.         "id": "0573d01071f92415",
  4100.         "type": "mysql",
  4101.         "z": "303c9862.ddfff8",
  4102.         "mydb": "76a2023f.8c254c",
  4103.         "name": "",
  4104.         "x": 1806.866554260254,
  4105.         "y": 3827.5335006713867,
  4106.         "wires": [
  4107.             []
  4108.         ]
  4109.     },
  4110.     {
  4111.         "id": "7ba79850621f4c7b",
  4112.         "type": "function",
  4113.         "z": "303c9862.ddfff8",
  4114.         "name": "data misc",
  4115.         "func": "if (msg.payload.type == \"data misc\") {\n    const dataBatteries = msg.payload.data.batteries;\n    const dataUnits = msg.payload.data.units;\n\n    msg.payload = msg.payload.data.columnValues;\n\n    delete msg.payload.batteries;\n\n    const columns = Object.keys(msg.payload).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n    node.send({\n        topic: `INSERT INTO pv_battery_data_misc SET log_date = NOW(), ${columns} ON DUPLICATE KEY UPDATE uuid=uuid`,\n        payload: Object.values(msg.payload),\n    });\n\n    for (const dataBattery of dataBatteries) {\n        const columns = Object.keys(dataBattery).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_data_misc_battery SET ${columns} ON DUPLICATE KEY UPDATE parent_uuid=parent_uuid`,\n            payload: Object.values(dataBattery),\n        });\n    }\n\n    for (const dataUnit of dataUnits) {\n        const columns = Object.keys(dataUnit).map((column) => `\\`${column}\\` = ?`).join(', ');\n\n        node.send({\n            topic: `INSERT INTO pv_battery_data_misc_unit SET ${columns} ON DUPLICATE KEY UPDATE parent_uuid=parent_uuid`,\n            payload: Object.values(dataUnit),\n        });\n    }\n}",
  4116.         "outputs": 1,
  4117.         "noerr": 0,
  4118.         "initialize": "",
  4119.         "finalize": "",
  4120.         "libs": [],
  4121.         "x": 1655.199951171875,
  4122.         "y": 3864.199951171875,
  4123.         "wires": [
  4124.             [
  4125.                 "12e5b0aece5a3d30"
  4126.             ]
  4127.         ]
  4128.     },
  4129.     {
  4130.         "id": "12e5b0aece5a3d30",
  4131.         "type": "mysql",
  4132.         "z": "303c9862.ddfff8",
  4133.         "mydb": "76a2023f.8c254c",
  4134.         "name": "",
  4135.         "x": 1811.866554260254,
  4136.         "y": 3863.5335006713867,
  4137.         "wires": [
  4138.             []
  4139.         ]
  4140.     },
  4141.     {
  4142.         "id": "5d943b18bc939561",
  4143.         "type": "inject",
  4144.         "z": "303c9862.ddfff8",
  4145.         "name": "",
  4146.         "props": [
  4147.             {
  4148.                 "p": "payload"
  4149.             },
  4150.             {
  4151.                 "p": "topic",
  4152.                 "vt": "str"
  4153.             }
  4154.         ],
  4155.         "repeat": "",
  4156.         "crontab": "",
  4157.         "once": true,
  4158.         "onceDelay": "5",
  4159.         "topic": "",
  4160.         "payload": "",
  4161.         "payloadType": "date",
  4162.         "x": 525.2000160217285,
  4163.         "y": 431.199990272522,
  4164.         "wires": [
  4165.             [
  4166.                 "841afa9422f1d270"
  4167.             ]
  4168.         ]
  4169.     },
  4170.     {
  4171.         "id": "841afa9422f1d270",
  4172.         "type": "function",
  4173.         "z": "303c9862.ddfff8",
  4174.         "name": "",
  4175.         "func": "const af = global.get('actionflows');\n\nconst resMsg = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: \"SELECT * FROM pv_rooftop\",\n    values: [],\n});\n\nfor (const rooftop of resMsg.payload) {\n    flow.set(`pv_rooftop_power${rooftop.id}`, rooftop.power)\n}\n",
  4176.         "outputs": 1,
  4177.         "noerr": 0,
  4178.         "initialize": "",
  4179.         "finalize": "",
  4180.         "libs": [],
  4181.         "x": 691.2001113891602,
  4182.         "y": 431.1999988555908,
  4183.         "wires": [
  4184.             []
  4185.         ]
  4186.     },
  4187.     {
  4188.         "id": "78a4b4b587a2cc69",
  4189.         "type": "comment",
  4190.         "z": "303c9862.ddfff8",
  4191.         "name": "Configuration",
  4192.         "info": "",
  4193.         "x": 289.1999816894531,
  4194.         "y": 42.19999694824219,
  4195.         "wires": []
  4196.     },
  4197.     {
  4198.         "id": "34d36175b8acdfbf",
  4199.         "type": "comment",
  4200.         "z": "303c9862.ddfff8",
  4201.         "name": "Solcast",
  4202.         "info": "",
  4203.         "x": 380.1999855041504,
  4204.         "y": 135.1999969482422,
  4205.         "wires": []
  4206.     },
  4207.     {
  4208.         "id": "591c5614b4927551",
  4209.         "type": "comment",
  4210.         "z": "303c9862.ddfff8",
  4211.         "name": "strings power",
  4212.         "info": "",
  4213.         "x": 336.20001220703125,
  4214.         "y": 405.199990272522,
  4215.         "wires": []
  4216.     },
  4217.     {
  4218.         "id": "ad7b6572f85bdecc",
  4219.         "type": "ui_text",
  4220.         "z": "303c9862.ddfff8",
  4221.         "group": "45ecb2bfdd21bc90",
  4222.         "order": 1,
  4223.         "width": 0,
  4224.         "height": 0,
  4225.         "name": "",
  4226.         "label": "Sunrise",
  4227.         "format": "{{msg.payload}}",
  4228.         "layout": "row-spread",
  4229.         "className": "",
  4230.         "x": 2440.4000606536865,
  4231.         "y": 561.2000370025635,
  4232.         "wires": []
  4233.     },
  4234.     {
  4235.         "id": "889fe2593fed52ab",
  4236.         "type": "function",
  4237.         "z": "303c9862.ddfff8",
  4238.         "name": "function 21",
  4239.         "func": "const dt = new Date(msg.payload.times.sunrise.ts);\nmsg.payload = `${dt.getHours()}:${String(dt.getMinutes()).padStart(2, '0')}`;\nreturn msg;",
  4240.         "outputs": 1,
  4241.         "noerr": 0,
  4242.         "initialize": "",
  4243.         "finalize": "",
  4244.         "libs": [],
  4245.         "x": 2281.400058746338,
  4246.         "y": 562.2000360488892,
  4247.         "wires": [
  4248.             [
  4249.                 "ad7b6572f85bdecc"
  4250.             ]
  4251.         ]
  4252.     },
  4253.     {
  4254.         "id": "d91fb2ef78f1148b",
  4255.         "type": "function",
  4256.         "z": "303c9862.ddfff8",
  4257.         "name": "function 22",
  4258.         "func": "const dt = new Date(msg.payload.times.sunset.ts);\nmsg.payload = `${dt.getHours()}:${String(dt.getMinutes()).padStart(2, '0')}`;\nreturn msg;",
  4259.         "outputs": 1,
  4260.         "noerr": 0,
  4261.         "initialize": "",
  4262.         "finalize": "",
  4263.         "libs": [],
  4264.         "x": 2285.39990234375,
  4265.         "y": 602.2000122070312,
  4266.         "wires": [
  4267.             [
  4268.                 "030fdb239a77bcb1"
  4269.             ]
  4270.         ]
  4271.     },
  4272.     {
  4273.         "id": "030fdb239a77bcb1",
  4274.         "type": "ui_text",
  4275.         "z": "303c9862.ddfff8",
  4276.         "group": "45ecb2bfdd21bc90",
  4277.         "order": 1,
  4278.         "width": 0,
  4279.         "height": 0,
  4280.         "name": "",
  4281.         "label": "Sunset",
  4282.         "format": "{{msg.payload}}",
  4283.         "layout": "row-spread",
  4284.         "className": "",
  4285.         "x": 2444.3999042510986,
  4286.         "y": 601.2000131607056,
  4287.         "wires": []
  4288.     },
  4289.     {
  4290.         "id": "af9f1be217dafccc",
  4291.         "type": "http request",
  4292.         "z": "303c9862.ddfff8",
  4293.         "d": true,
  4294.         "name": "",
  4295.         "method": "POST",
  4296.         "ret": "txt",
  4297.         "paytoqs": "ignore",
  4298.         "url": "http://45.92.239.28:27009/fve-gibon",
  4299.         "tls": "",
  4300.         "persist": false,
  4301.         "proxy": "",
  4302.         "insecureHTTPParser": false,
  4303.         "authType": "",
  4304.         "senderr": false,
  4305.         "headers": [],
  4306.         "x": 2254.200183868408,
  4307.         "y": 922.4001140594482,
  4308.         "wires": [
  4309.             [
  4310.                 "62a92fee9dced299"
  4311.             ]
  4312.         ]
  4313.     },
  4314.     {
  4315.         "id": "d26beecfc0830bfe",
  4316.         "type": "sub",
  4317.         "z": "303c9862.ddfff8",
  4318.         "d": true,
  4319.         "name": "pv.inverter.record",
  4320.         "topic": "pv.inverter.record",
  4321.         "x": 1697.200050354004,
  4322.         "y": 932.4000539779663,
  4323.         "wires": [
  4324.             [
  4325.                 "7b32339e1d189737"
  4326.             ]
  4327.         ]
  4328.     },
  4329.     {
  4330.         "id": "7b32339e1d189737",
  4331.         "type": "function",
  4332.         "z": "303c9862.ddfff8",
  4333.         "d": true,
  4334.         "name": "function 24",
  4335.         "func": "msg.payload = {\n    pvPowerPerWattPeak:  {\n        southEast: msg.payload.pv_ppv1 / 7200,\n        southWest: msg.payload.pv_ppv2 / 5400,\n    }\n};\nreturn msg;",
  4336.         "outputs": 1,
  4337.         "noerr": 0,
  4338.         "initialize": "",
  4339.         "finalize": "",
  4340.         "libs": [],
  4341.         "x": 1908.20015335083,
  4342.         "y": 929.4000844955444,
  4343.         "wires": [
  4344.             [
  4345.                 "863414f1f720bfc1"
  4346.             ]
  4347.         ]
  4348.     },
  4349.     {
  4350.         "id": "62a92fee9dced299",
  4351.         "type": "debug",
  4352.         "z": "303c9862.ddfff8",
  4353.         "d": true,
  4354.         "name": "debug 22",
  4355.         "active": false,
  4356.         "tosidebar": true,
  4357.         "console": false,
  4358.         "tostatus": false,
  4359.         "complete": "false",
  4360.         "statusVal": "",
  4361.         "statusType": "auto",
  4362.         "x": 2394.199966430664,
  4363.         "y": 922.4000844955444,
  4364.         "wires": []
  4365.     },
  4366.     {
  4367.         "id": "54ee2ce5980e7fd8",
  4368.         "type": "comment",
  4369.         "z": "303c9862.ddfff8",
  4370.         "d": true,
  4371.         "name": "preposlani vykonu k otovi",
  4372.         "info": "",
  4373.         "x": 1679.200050354004,
  4374.         "y": 858.4000520706177,
  4375.         "wires": []
  4376.     },
  4377.     {
  4378.         "id": "863414f1f720bfc1",
  4379.         "type": "delay",
  4380.         "z": "303c9862.ddfff8",
  4381.         "d": true,
  4382.         "name": "",
  4383.         "pauseType": "rate",
  4384.         "timeout": "5",
  4385.         "timeoutUnits": "seconds",
  4386.         "rate": "1",
  4387.         "nbRateUnits": "10",
  4388.         "rateUnits": "second",
  4389.         "randomFirst": "1",
  4390.         "randomLast": "5",
  4391.         "randomUnits": "seconds",
  4392.         "drop": true,
  4393.         "allowrate": false,
  4394.         "outputs": 1,
  4395.         "x": 2085.2000579833984,
  4396.         "y": 923.4001140594482,
  4397.         "wires": [
  4398.             [
  4399.                 "af9f1be217dafccc"
  4400.             ]
  4401.         ]
  4402.     },
  4403.     {
  4404.         "id": "6d168fe4f0603839",
  4405.         "type": "inject",
  4406.         "z": "303c9862.ddfff8",
  4407.         "name": "",
  4408.         "props": [
  4409.             {
  4410.                 "p": "payload"
  4411.             },
  4412.             {
  4413.                 "p": "topic",
  4414.                 "vt": "str"
  4415.             }
  4416.         ],
  4417.         "repeat": "60",
  4418.         "crontab": "",
  4419.         "once": false,
  4420.         "onceDelay": 0.1,
  4421.         "topic": "",
  4422.         "payload": "",
  4423.         "payloadType": "date",
  4424.         "x": 1891.199951171875,
  4425.         "y": 736.2000122070312,
  4426.         "wires": [
  4427.             [
  4428.                 "29a390381fb42140"
  4429.             ]
  4430.         ]
  4431.     },
  4432.     {
  4433.         "id": "29a390381fb42140",
  4434.         "type": "function",
  4435.         "z": "303c9862.ddfff8",
  4436.         "name": "function 28",
  4437.         "func": "const sunPositionNow = flow.get('sunPositionNow');\nconst sunPositionSunrise = flow.get('sunPositionSunrise');\nconst sunPositionSunset = flow.get('sunPositionSunset');\n\nmsg.payload = {\n    altitude: sunPositionNow.altitudeDegrees,\n    maxAltitude: sunPositionNow.positionAtSolarNoon.altitudeDegrees,\n    afternoon: sunPositionNow.ts > (sunPositionSunset.ts + sunPositionSunrise.ts) / 2,\n}\nreturn msg;",
  4438.         "outputs": 1,
  4439.         "noerr": 0,
  4440.         "initialize": "",
  4441.         "finalize": "",
  4442.         "libs": [],
  4443.         "x": 2074.1999588012695,
  4444.         "y": 738.2000122070312,
  4445.         "wires": [
  4446.             [
  4447.                 "a9c95c7334c170ae"
  4448.             ]
  4449.         ]
  4450.     },
  4451.     {
  4452.         "id": "a9c95c7334c170ae",
  4453.         "type": "ui_template",
  4454.         "z": "303c9862.ddfff8",
  4455.         "group": "45ecb2bfdd21bc90",
  4456.         "name": "",
  4457.         "order": 2,
  4458.         "width": 0,
  4459.         "height": 0,
  4460.         "format": "<div class=\"sun-path\">\n\t<canvas id=\"sunPathCanvas\" width=300 height=200></canvas>\n</div>\n\n<script>\n\t(function(scope) {\n\tscope.$watch('msg.payload', function(payload) {\n\t\tif (payload) {\n\t\t\tconsole.log(payload);\n\n\t\t\tclass SunPathUtils {\n\t\t\t\tstatic clearCanvas(ctx) {\n\t\t\t\t\tctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n\t\t\t\t}\n\n\t\t\t\tstatic drawAlignedText(ctx, text, x, y, align) {\n\t\t\t\t\tconst metrics = ctx.measureText(text);\n\t\t\t\t\tconst textWidth = metrics.width;\n\n\t\t\t\t\tif (align == 'center') {\n\t\t\t\t\t\tctx.fillText(text, x - (textWidth / 2), y);\n\t\t\t\t\t}else if (align == 'right') {\n\t\t\t\t\t\tctx.fillText(text, x - textWidth, y);\n\t\t\t\t\t}else {\n\t\t\t\t\t\tctx.fillText(text, x, y);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tstatic deg2rad(angle) {\n\t\t\t\t\treturn angle / 180 * Math.PI;\n\t\t\t\t}\n\n\t\t\t\tstatic getAngle(x1, y1, x2, y2) {\n\t\t\t\t\tconst dx = x2 - x1;\n\t\t\t\t\tconst dy = y2 - y1;\n\t\t\t\t\tconst radians = Math.atan2(dy, dx);\n\t\t\t\t\tconst angle = radians * 180 / Math.PI;\n\t\t\t\t\treturn angle;\n\t\t\t\t}\n\n\t\t\t\tstatic interceptCircleLine(circle, line) {\n\t\t\t\t\tlet a, b, c, d, u1, u2, ret, retP1, retP2, v1, v2;\n\t\t\t\t\tv1 = {};\n\t\t\t\t\tv2 = {};\n\t\t\t\t\tv1.x = line.p2.x - line.p1.x;\n\t\t\t\t\tv1.y = line.p2.y - line.p1.y;\n\t\t\t\t\tv2.x = line.p1.x - circle.center.x;\n\t\t\t\t\tv2.y = line.p1.y - circle.center.y;\n\t\t\t\t\tb = (v1.x * v2.x + v1.y * v2.y);\n\t\t\t\t\tc = 2 * (v1.x * v1.x + v1.y * v1.y);\n\t\t\t\t\tb *= -2;\n\t\t\t\t\td = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - circle.radius * circle.radius));\n\t\t\t\t\tif(isNaN(d)){ // no intercept\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\tu1 = (b - d) / c;  // these represent the unit distance of point one and two on the line\n\t\t\t\t\tu2 = (b + d) / c;    \n\t\t\t\t\tretP1 = {};   // return points\n\t\t\t\t\tretP2 = {}  \n\t\t\t\t\tret = []; // return array\n\t\t\t\t\tif(u1 <= 1 && u1 >= 0){  // add point if on the line segment\n\t\t\t\t\t\tretP1.x = line.p1.x + v1.x * u1;\n\t\t\t\t\t\tretP1.y = line.p1.y + v1.y * u1;\n\t\t\t\t\t\tret[0] = retP1;\n\t\t\t\t\t}\n\t\t\t\t\tif(u2 <= 1 && u2 >= 0){  // second add point if on the line segment\n\t\t\t\t\t\tretP2.x = line.p1.x + v1.x * u2;\n\t\t\t\t\t\tretP2.y = line.p1.y + v1.y * u2;\n\t\t\t\t\t\tret[ret.length] = retP2;\n\t\t\t\t\t}\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tstatic createLineByAngle(point, angle, length) {\n\t\t\t\t\tconst angleInRadians = angle * Math.PI / 180;\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tp1: {\n\t\t\t\t\t\t\tx: point.x,\n\t\t\t\t\t\t\ty: point.y,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tp2: {\n\t\t\t\t\t\t\tx: point.x - length*Math.cos(angleInRadians),\n\t\t\t\t\t\t\ty: point.y - length*Math.sin(angleInRadians),\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tstatic getLineAngle(x1, y1, x2, y2) {\n\t\t\t\t\tconst dx = x2 - x1;\n\t\t\t\t\tconst dy = y2 - y1;\n\t\t\t\t\treturn Math.atan2(dy, dx) * 180 / Math.PI;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclass SunPathVisualizer {\n\t\t\t\t// Set the ellipse parameters\n\t\t\t\tconfig = {\n\t\t\t\t\tcenterX: 170,\n\t\t\t\t\tcenterY: 250,\n\t\t\t\t\tradius: 250,\n\t\t\t\t};\n\n\t\t\t\tctx = null;\n\n\t\t\t\tconstructor(options, ctx) {\n\t\t\t\t\tthis.options = options;\n\t\t\t\t\tthis.ctx = ctx;\n\t\t\t\t\tthis.config = {\n\t\t\t\t\t\tcenterX: options.size / 2,\n\t\t\t\t\t\tcenterY: options.size,\n\t\t\t\t\t\tradius: options.size,\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.azimuth = null;\n\t\t\t\t}\n\n\t\t\t\tcalcAngleOffsetByAltitude(altitude) {\n\t\t\t\t\t// vysku oblouku kruznice udelat pomerove vuci maximalni altitude.\n\t\t\t\t\t// Maximum je altitude 90° a v tom pripade se vykresli plny pulkruh\n\t\t\t\t\t// Pokud je altitude 45° tedy polovina z 90° tak vyska oblouku bude polovicni vuci pulkruhu\n\t\t\t\t\t//angleOffset = 90 * (altitude - 90) / 100;\n\t\t\t\t\tconst angleOffset = altitude;\n\n\t\t\t\t\treturn angleOffset;\n\t\t\t\t}\n\n\t\t\t\tdrawSunPath(ctx, maxAltitude) {\n\t\t\t\t\t// Begin drawing the ellipse path\n\t\t\t\t\tctx.beginPath();\n\n\t\t\t\t\tconst angleOffsetRad = SunPathUtils.deg2rad(this.calcAngleOffsetByAltitude(maxAltitude));\n\n\t\t\t\t\t// Draw the ellipse\n\t\t\t\t\tctx.ellipse(this.config.centerX, this.config.centerY, this.config.radius, this.config.radius, 0, Math.PI/2*3 - angleOffsetRad, Math.PI/2*3 + angleOffsetRad);\n\n\t\t\t\t\t// Set the stroke and fill styles\n\t\t\t\t\tctx.strokeStyle = \"black\";\n\t\t\t\t\tctx.lineWidth = 1;\n\t\t\t\t\tctx.fillStyle = \"lightblue\";\n\n\t\t\t\t\t// Fill and stroke the ellipse\n\t\t\t\t\tctx.fill();\n\t\t\t\t\tctx.stroke();\n\t\t\t\t}\n\n\t\t\t\tcalcPointByAzimuth(azimuth) {\n\t\t\t\t\tconst phi = 0;\n\n\t\t\t\t\tconst angle = azimuth - 90;\n\n\t\t\t\t\t// Calculate the angle between the X-axis and the point on the arc\n\t\t\t\t\tconst theta = (angle) * (Math.PI / 180);\n\n\t\t\t\t\t// Calculate the (x, y) position of the point on the arc\n\t\t\t\t\tconst x = this.config.centerX + this.config.radius * Math.cos(theta) * Math.cos(phi) - this.config.radius * Math.sin(theta) * Math.sin(phi);\n\t\t\t\t\tconst y = this.config.centerY + this.config.radius * Math.cos(theta) * Math.sin(phi) + this.config.radius * Math.sin(theta) * Math.cos(phi);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tx, y,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcalcGround(maxAltitude) {\n\t\t\t\t\t// potrebujeme zjistit, kde se nachazi zem\n\n\t\t\t\t\t// nejdrive podle altitude zjistime angle offset\n\t\t\t\t\tconst angleOffset = this.calcAngleOffsetByAltitude(maxAltitude);\n\t\t\t\t\t//console.log({angleOffset});\n\n\t\t\t\t\t// a podle angleOffset, ktery bude predstavovat azimuth, tak vypocitame pozici bodu\n\t\t\t\t\tconst point1 = this.calcPointByAzimuth(-angleOffset);\n\t\t\t\t\tconst point2 = this.calcPointByAzimuth(angleOffset);\n\n\t\t\t\t\t// vypocitame vysku oblouku\n\t\t\t\t\tconst arcHeight = this.config.radius - (this.config.centerY - point1.y);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpoint1,\n\t\t\t\t\t\tpoint2,\n\t\t\t\t\t\tarcHeight,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tcalcGroundBasePoint(maxAltitude, afternoon) {\n\t\t\t\t\tconst ground = this.calcGround(maxAltitude);\n\n\t\t\t\t\t// zname vysku oblouku a uhel max altitude. Podle toho vypocitame do jakeho bodu to dopadne na zemi\n\t\t\t\t\tconst sideLengthRatio = Math.sin(SunPathUtils.deg2rad(90 - maxAltitude)) / Math.sin(SunPathUtils.deg2rad(maxAltitude));\n\t\t\t\t\tconst basePointOffsetFromCenter = ground.arcHeight * sideLengthRatio;\n\t\t\t\t\t//console.log({basePointOffsetFromCenter});\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\ty: ground.point1.y,\n\t\t\t\t\t\tx: afternoon ? this.config.centerX - basePointOffsetFromCenter : this.config.centerX + basePointOffsetFromCenter,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tdrawGround(ctx, maxAltitude, afternoon) {\n\t\t\t\t\tconst ground = this.calcGround(maxAltitude);\n\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.strokeStyle = \"black\";\n\t\t\t\t\tctx.lineWidth = 3;\n\t\t\t\t\tctx.moveTo(ground.point1.x, ground.point1.y);\n\t\t\t\t\tctx.lineTo(ground.point2.x, ground.point2.y);\n\t\t\t\t\tctx.stroke();\n\n\t\t\t\t\t// vykreslit bod\n\t\t\t\t\tconst groundBasePoint = this.calcGroundBasePoint(maxAltitude, afternoon);\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.arc(groundBasePoint.x, groundBasePoint.y, 5, 0, 2*Math.PI);\n\t\t\t\t\t// Set the fill color and fill the circle\n\t\t\t\t\tctx.fillStyle = \"#555\";\n\t\t\t\t\tctx.fill();\n\n\t\t\t\t\tctx.font = '13px Arial';\n\t\t\t\t\tctx.fillStyle = 'black';\n\t\t\t\t\tSunPathUtils.drawAlignedText(ctx, `sunset`, ground.point1.x, ground.point1.y + 15, 'left');\n\t\t\t\t\tSunPathUtils.drawAlignedText(ctx, `sunrise`, ground.point2.x, ground.point2.y + 15, 'right');\n\t\t\t\t}\n\n\t\t\t\tdrawSun(ctx, azimuth) {\n\t\t\t\t\tconst sunPosition = this.calcPointByAzimuth(azimuth);\n\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.arc(sunPosition.x, sunPosition.y, 10, 0, 2*Math.PI);\n\n\t\t\t\t\t// Set the fill color and fill the circle\n\t\t\t\t\tctx.fillStyle = \"yellow\";\n\t\t\t\t\tctx.fill();\n\t\t\t\t\tctx.stroke();\n\t\t\t\t}\n\n\t\t\t\tdrawSunAngle(ctx, maxAltitude, azimuth) {\n\t\t\t\t\t// zjistit, kde je base bod na zemi\n\t\t\t\t\tconst groundBasePoint = this.calcGroundBasePoint(maxAltitude, azimuth > 0);\n\n\t\t\t\t\t// zjistit bod na ceste slunce podle azimutu\n\t\t\t\t\tconst sunPosition = this.calcPointByAzimuth(azimuth);\n\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.setLineDash([2, 4]);\n\t\t\t\t\tctx.strokeStyle = \"#888\";\n\t\t\t\t\tctx.lineWidth = 1;\n\t\t\t\t\tctx.moveTo(groundBasePoint.x, groundBasePoint.y);\n\t\t\t\t\tctx.lineTo(sunPosition.x, sunPosition.y);\n\t\t\t\t\tctx.stroke();\n\t\t\t\t\tctx.setLineDash([]);\n\n\t\t\t\t\tconst angle = SunPathUtils.getAngle(Math.min(groundBasePoint.x, sunPosition.x), sunPosition.y, Math.max(groundBasePoint.x, sunPosition.x), groundBasePoint.y);\n\t\t\t\t\t//console.log({angle})\n\n\t\t\t\t\tctx.font = '15px Arial';\n\t\t\t\t\tctx.fillStyle = 'black';\n\t\t\t\t\tSunPathUtils.drawAlignedText(ctx, `${Math.round(angle)}°`, groundBasePoint.x, groundBasePoint.y - 15, 'center');\n\t\t\t\t}\n\n\t\t\t\tcalcSunPositionByAltitude(maxAltitude, altitude, afternoon) {\n\t\t\t\t\t// zjistit, kde je base bod na zemi\n\t\t\t\t\tconst groundBasePoint = this.calcGroundBasePoint(maxAltitude, afternoon);\n\n\t\t\t\t\t// zname bod na zemi a uhel smerem k oblouku kudy jde slunce\n\t\t\t\t\t// podle toho vypocitame pozici na oblouku\n\n\t\t\t\t\t// sestrojime usecku podle pozadovaneho uhlu\n\t\t\t\t\tconst line = SunPathUtils.createLineByAngle(groundBasePoint, afternoon ? 180 - altitude : altitude, 2*this.config.radius);\n\n\t\t\t\t\t// vypocitame prusecik\n\t\t\t\t\tconst intersections = SunPathUtils.interceptCircleLine({center: {x: this.config.centerX, y: this.config.centerY}, radius: this.config.radius}, line);\n\n\t\t\t\t\t//console.log(intersections);\n\n\t\t\t\t\tif (intersections.length > 0) {\n\t\t\t\t\t\t// for (const intersection of intersections) {\n\t\t\t\t\t\t// \tctx.beginPath();\n\t\t\t\t\t\t// \tctx.arc(intersection.x, intersection.y, 10, 0, 2*Math.PI);\n\n\t\t\t\t\t\t// \t// Set the fill color and fill the circle\n\t\t\t\t\t\t// \tctx.fillStyle = \"red\";\n\t\t\t\t\t\t// \tctx.fill();\n\t\t\t\t\t\t// }\n\n\t\t\t\t\t\tconst intersection = intersections[0];\n\n\t\t\t\t\t\t// zjistime jaky uhel svira stred kriznice a bod na ceste slunce\n\t\t\t\t\t\tconst angle = SunPathUtils.getLineAngle(intersection.x, intersection.y, this.config.centerX, this.config.centerY);\n\t\t\t\t\t\t//console.log(angle);\n\n\t\t\t\t\t\t// prepocitame na azimut\n\t\t\t\t\t\tthis.azimuth = -1*(90 - angle);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcalcSizes(maxAltitude, azimuth, targetGroundWidth, targetGroundY) {\n\t\t\t\t\t// nahodit nejaky radius\n\t\t\t\t\tthis.config.radius = this.config.radius = 100;\n\t\t\t\t\tthis.config.centerY = 1000;\n\n\t\t\t\t\t// vypocitat polohu zeme\n\t\t\t\t\tlet ground = this.calcGround(maxAltitude, azimuth > 0);\n\t\t\t\t\tconst groundWidth = ground.point2.x - ground.point1.x;\n\n\t\t\t\t\t// podle vypoctene polohy zeme spocitat jak ma byt velky radius\n\t\t\t\t\tconst ratio = targetGroundWidth / groundWidth;\n\t\t\t\t\tconst radius = ratio * this.config.radius;\n\t\t\t\t\tthis.config.radius = this.config.radius = radius;\n\n\t\t\t\t\t// zjistit kam vychazi pozice zeme\n\t\t\t\t\tground = this.calcGround(maxAltitude, azimuth > 0);\n\t\t\t\t\t// soupnout stred kruhu podle pozadovane pozice zeme\n\t\t\t\t\tthis.config.centerY = 1000 + (targetGroundY - ground.point1.y);\n\t\t\t\t}\n\n\t\t\t\tdraw(maxAltitude, altitude, afternoon) {\n\t\t\t\t\tSunPathUtils.clearCanvas(this.ctx);\n\n\t\t\t\t\tthis.calcSizes(maxAltitude, 0, this.options.size - 2*10, this.options.size / 2);\n\n\t\t\t\t\tthis.calcSunPositionByAltitude(maxAltitude, altitude, afternoon);\n\n\t\t\t\t\tthis.azimuth = -this.azimuth;\n\n\t\t\t\t\tthis.drawSunPath(this.ctx, maxAltitude);\n\t\t\t\t\tthis.drawGround(this.ctx, maxAltitude, this.azimuth > 0);\n\t\t\t\t\tthis.drawSunAngle(this.ctx, maxAltitude, this.azimuth);\n\t\t\t\t\tthis.drawSun(this.ctx, this.azimuth);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t\n\t\t\t// Get the canvas element and its 2D drawing context\n\t\t\tconst sunPathCanvas = document.getElementById(\"sunPathCanvas\");\n\t\t\tconst sunPathCanvasCtx = sunPathCanvas.getContext(\"2d\");\n\t\t\tconst sunPathVisualizer = new SunPathVisualizer({size: 300}, sunPathCanvasCtx);\n\n\t\t\tsunPathVisualizer.draw(payload.maxAltitude, payload.altitude, payload.afternoon);\n\t\t}\n\t});\n})(scope);\n</script>\n\n<style>\n\t.sun-path {}\n\n\tcanvas {}\n</style>",
  4461.         "storeOutMessages": true,
  4462.         "fwdInMessages": true,
  4463.         "resendOnRefresh": true,
  4464.         "templateScope": "local",
  4465.         "className": "sun-path-widget",
  4466.         "x": 2247.200080871582,
  4467.         "y": 740.2000141143799,
  4468.         "wires": [
  4469.             []
  4470.         ]
  4471.     },
  4472.     {
  4473.         "id": "505d362ca3ebe622",
  4474.         "type": "inject",
  4475.         "z": "303c9862.ddfff8",
  4476.         "name": "",
  4477.         "props": [
  4478.             {
  4479.                 "p": "payload"
  4480.             },
  4481.             {
  4482.                 "p": "todayOffset",
  4483.                 "v": "0",
  4484.                 "vt": "str"
  4485.             }
  4486.         ],
  4487.         "repeat": "3600",
  4488.         "crontab": "",
  4489.         "once": true,
  4490.         "onceDelay": 0.1,
  4491.         "topic": "",
  4492.         "payload": "",
  4493.         "payloadType": "date",
  4494.         "x": 1187.1999626159668,
  4495.         "y": 4943.399903297424,
  4496.         "wires": [
  4497.             [
  4498.                 "b62f4d1a7bf9b908"
  4499.             ]
  4500.         ]
  4501.     },
  4502.     {
  4503.         "id": "b62f4d1a7bf9b908",
  4504.         "type": "function",
  4505.         "z": "303c9862.ddfff8",
  4506.         "name": "",
  4507.         "func": "const af = global.get('actionflows');\n\nconst BILLING_PERIOD_DATE_FROM = flow.get('billing_period_date_from');\n\nlet q;\nconst resMsgGridStats = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: q=`\n        SELECT date, ROUND(exported_kwh - imported_kwh) AS balance_kwh FROM (\n            SELECT\n                DATE(log_date) AS date,\n                @running_total_imported := IFNULL(@running_total_imported, 0) + imported AS imported_kwh,\n                @running_total_exported := IFNULL(@running_total_exported, 0) + exported AS exported_kwh\n            FROM\n                pv_daily_stats,\n                (SELECT @running_total_imported := 0, @running_total_exported := 0) AS init_vars\n            WHERE log_date >= IF(STR_TO_DATE(CONCAT('${BILLING_PERIOD_DATE_FROM}', YEAR(NOW())),'%d.%m.%Y') < NOW(), STR_TO_DATE(CONCAT('${BILLING_PERIOD_DATE_FROM}', YEAR(NOW())),'%d.%m.%Y'), STR_TO_DATE(CONCAT('${BILLING_PERIOD_DATE_FROM}', YEAR(NOW())-1),'%d.%m.%Y'))\n        ) a\n    `,\n    values: [],\n});\n// node.warn(q);\n//node.warn(resMsgGridStats);\n\n// odhad do budoucna\n// zjistime zda-li je den zuctovani (resp. predchozi den) v budoucnosti\n// pokud je den v budoucnosti (napr. dnes je 1.9.2023 a ten den je 31.12.2023)\n// tak vezmeme z minulosti 2.9.2022-31.12.2022\nconst [day, month] = BILLING_PERIOD_DATE_FROM.split('.').map(Number);\nconst today = new Date();\nconst billingPeriodEndDate = new Date(today.getFullYear(), month - 1, day - 1);\n// opravit, aby to bylo tento rok, kdyby to nahodou preteklo do predchoziho\nbillingPeriodEndDate.setFullYear(today.getFullYear());\nconst billingPeriodEndDateInFuture = billingPeriodEndDate > today;\n\n\nconst resMsgEstimatedGridStats = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT date, ROUND(exported_kwh - imported_kwh) AS balance_kwh FROM (\n            SELECT\n                DATE(log_date)+INTERVAL 1 YEAR AS date,\n                @running_total_imported := IFNULL(@running_total_imported, 0) + imported AS imported_kwh,\n                @running_total_exported := IFNULL(@running_total_exported, 0) + exported AS exported_kwh\n            FROM\n                pv_daily_stats,\n                (SELECT @running_total_imported := 0, @running_total_exported := 0) AS init_vars\n            WHERE\n                log_date >= DATE(NOW()) + INTERVAL 1 DAY - INTERVAL 1 YEAR\n                AND\n                log_date <= STR_TO_DATE('${billingPeriodEndDate.getDate()}.${billingPeriodEndDate.getMonth() + 1}.${billingPeriodEndDateInFuture ? billingPeriodEndDate.getFullYear() - 1 : billingPeriodEndDate.getFullYear()}', '%d.%m.%Y')\n        ) a\n    `,\n    values: [],\n});\n// node.warn(q);\nnode.warn(resMsgEstimatedGridStats);\n\n\n// zkombinovat\nmsg.payload = resMsgGridStats.payload;\n\nconst lastGridStatsRecord = resMsgGridStats.payload[resMsgGridStats.payload.length-1];\n// node.warn({ lastGridStatsRecord})\nfor (const record of resMsgEstimatedGridStats.payload) {\n    msg.payload.push({\n        date: record.date,\n        estimated_balance_kwh: lastGridStatsRecord.balance_kwh + record.balance_kwh,\n    });\n}\n\nreturn msg;\nmsg.payload = {\n    import: Math.round(resMsgGridStats.payload[0].imported_kwh),\n    export: Math.round(resMsgGridStats.payload[0].exported_kwh),\n}\n\nreturn msg;\n",
  4508.         "outputs": 1,
  4509.         "noerr": 0,
  4510.         "initialize": "",
  4511.         "finalize": "",
  4512.         "libs": [],
  4513.         "x": 1376.1999702453613,
  4514.         "y": 4943.399971961975,
  4515.         "wires": [
  4516.             [
  4517.                 "32aefffe0f2a21de",
  4518.                 "a29916e511733d1c"
  4519.             ]
  4520.         ]
  4521.     },
  4522.     {
  4523.         "id": "32aefffe0f2a21de",
  4524.         "type": "debug",
  4525.         "z": "303c9862.ddfff8",
  4526.         "name": "debug 25",
  4527.         "active": true,
  4528.         "tosidebar": true,
  4529.         "console": false,
  4530.         "tostatus": false,
  4531.         "complete": "false",
  4532.         "statusVal": "",
  4533.         "statusType": "auto",
  4534.         "x": 1562.2000617980957,
  4535.         "y": 4882.399729728699,
  4536.         "wires": []
  4537.     },
  4538.     {
  4539.         "id": "a29916e511733d1c",
  4540.         "type": "actionflows",
  4541.         "z": "303c9862.ddfff8",
  4542.         "info": "Describe your action API here.",
  4543.         "untilproptype": "num",
  4544.         "proptype": "msg",
  4545.         "name": "core.dashboard.graphize",
  4546.         "prop": "loop",
  4547.         "untilprop": 0,
  4548.         "until": "gt",
  4549.         "loop": "none",
  4550.         "scope": "global",
  4551.         "perf": false,
  4552.         "seq": false,
  4553.         "x": 1607.2000617980957,
  4554.         "y": 4934.399731636047,
  4555.         "wires": [
  4556.             [
  4557.                 "f29f56da9f3fa0b7"
  4558.             ]
  4559.         ]
  4560.     },
  4561.     {
  4562.         "id": "f29f56da9f3fa0b7",
  4563.         "type": "ui_chart",
  4564.         "z": "303c9862.ddfff8",
  4565.         "name": "",
  4566.         "group": "802549dc0ec48149",
  4567.         "order": 9,
  4568.         "width": 0,
  4569.         "height": 0,
  4570.         "label": "I/E Balance (Billing Period) (kWh)",
  4571.         "chartType": "line",
  4572.         "legend": "false",
  4573.         "xformat": "D/M",
  4574.         "interpolate": "linear",
  4575.         "nodata": "",
  4576.         "dot": false,
  4577.         "ymin": "",
  4578.         "ymax": "",
  4579.         "removeOlder": "50",
  4580.         "removeOlderPoints": "",
  4581.         "removeOlderUnit": "604800",
  4582.         "cutout": 0,
  4583.         "useOneColor": false,
  4584.         "useUTC": false,
  4585.         "colors": [
  4586.             "#1f77b4",
  4587.             "#ffee2e",
  4588.             "#ff7f0e",
  4589.             "#2ca02c",
  4590.             "#98df8a",
  4591.             "#d62728",
  4592.             "#ff9896",
  4593.             "#9467bd",
  4594.             "#c5b0d5"
  4595.         ],
  4596.         "outputs": 1,
  4597.         "useDifferentColor": false,
  4598.         "className": "",
  4599.         "x": 1906.2000579833984,
  4600.         "y": 4929.399730682373,
  4601.         "wires": [
  4602.             []
  4603.         ]
  4604.     },
  4605.     {
  4606.         "id": "d87a2bdcc3f484b0",
  4607.         "type": "function",
  4608.         "z": "303c9862.ddfff8",
  4609.         "name": "on off-grid mode only",
  4610.         "func": "const pvInverterRecord = flow.get('pvInverterRecord');\n\nif (pvInverterRecord.grid_mode_label != \"Connected to grid\") {\n    return msg;\n}",
  4611.         "outputs": 1,
  4612.         "noerr": 0,
  4613.         "initialize": "",
  4614.         "finalize": "",
  4615.         "libs": [],
  4616.         "x": 1101.2001037597656,
  4617.         "y": 3051.400192260742,
  4618.         "wires": [
  4619.             [
  4620.                 "e6a603d9.ee064"
  4621.             ]
  4622.         ]
  4623.     },
  4624.     {
  4625.         "id": "f2911eb919633a36",
  4626.         "type": "function",
  4627.         "z": "303c9862.ddfff8",
  4628.         "name": "Stav Baterie",
  4629.         "func": "const lastNotifiedBatteryStatus = flow.get(`lastNotifiedBatteryStatus`);\n\nlet batteryMode = `${ msg.payload.battery_mode_label }`;\nif (batteryMode == \"Charge\" || batteryMode == \"Discharge\" || batteryMode == \"Standby\") {\n    batteryMode = \"OK\";\n}\nlet currentBatteryStatus = `Změna stavu baterie: ${batteryMode}`;\n\nif (currentBatteryStatus != lastNotifiedBatteryStatus) {\n    flow.set(`lastNotifiedBatteryStatus`, currentBatteryStatus);\n    \n    msg.payload = { \"title\": currentBatteryStatus, \"text\" : \"\", \"event\" : \"speech\"}\n    node.send(msg);\n}\n",
  4630.         "outputs": 1,
  4631.         "noerr": 0,
  4632.         "initialize": "",
  4633.         "finalize": "",
  4634.         "libs": [],
  4635.         "x": 865.2000350952148,
  4636.         "y": 2987.400058746338,
  4637.         "wires": [
  4638.             [
  4639.                 "d71589dd249da60c"
  4640.             ]
  4641.         ]
  4642.     },
  4643.     {
  4644.         "id": "d71589dd249da60c",
  4645.         "type": "actionflows",
  4646.         "z": "303c9862.ddfff8",
  4647.         "info": "Describe your action API here.",
  4648.         "untilproptype": "num",
  4649.         "proptype": "msg",
  4650.         "name": "notify",
  4651.         "prop": "loop",
  4652.         "untilprop": 0,
  4653.         "until": "gt",
  4654.         "loop": "none",
  4655.         "scope": "global",
  4656.         "perf": false,
  4657.         "seq": false,
  4658.         "x": 1042.2001037597656,
  4659.         "y": 2983.4001903533936,
  4660.         "wires": [
  4661.             []
  4662.         ]
  4663.     },
  4664.     {
  4665.         "id": "e0b28e1766b868d0",
  4666.         "type": "inject",
  4667.         "z": "303c9862.ddfff8",
  4668.         "name": "",
  4669.         "props": [
  4670.             {
  4671.                 "p": "payload"
  4672.             },
  4673.             {
  4674.                 "p": "todayOffset",
  4675.                 "v": "0",
  4676.                 "vt": "str"
  4677.             }
  4678.         ],
  4679.         "repeat": "600",
  4680.         "crontab": "",
  4681.         "once": true,
  4682.         "onceDelay": 0.1,
  4683.         "topic": "",
  4684.         "payload": "",
  4685.         "payloadType": "date",
  4686.         "x": 445.1999816894531,
  4687.         "y": 1191.4000244140625,
  4688.         "wires": [
  4689.             [
  4690.                 "056ac2b68ca96547"
  4691.             ]
  4692.         ]
  4693.     },
  4694.     {
  4695.         "id": "056ac2b68ca96547",
  4696.         "type": "function",
  4697.         "z": "303c9862.ddfff8",
  4698.         "name": "",
  4699.         "func": "const af = global.get('actionflows');\n\nlet q;\nconst resMsgRows = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: q=`\n        WITH\n        pv_forecast_ranked_string1 AS (\n            SELECT time*(30*60)*1000 AS time, rooftop_kilowatts FROM (\n                SELECT\n                    FLOOR(UNIX_TIMESTAMP(forecast_from_date) / (30*60)) as \"time\",\n                    AVG(rooftop_kilowatts) AS \"rooftop_kilowatts\"\n                FROM (\n                    select\n                        pv_forecast.forecast_from_date, pv_forecast.kilowatts*pv_rooftop.power AS rooftop_kilowatts, ROW_NUMBER() OVER (PARTITION BY forecast_from_date ORDER BY request_date DESC) AS rn\n                    from\n                        pv_forecast\n                    JOIN pv_rooftop ON pv_rooftop.id = pv_rooftop_id\n                    where\n                        forecast_from_date >= DATE(NOW()) AND forecast_to_date < DATE(NOW()) + INTERVAL 1 DAY\n                        and pv_rooftop_id = 1\n                    order by forecast_from_date, request_date\n                ) t1\n                WHERE\n                    rn = 1\n                GROUP BY time\n            ) t2\n        ),\n        pv_forecast_ranked_string2 AS (\n            SELECT time*(30*60)*1000 AS time, rooftop_kilowatts FROM (\n                SELECT\n                    FLOOR(UNIX_TIMESTAMP(forecast_from_date) / (30*60)) as \"time\",\n                    AVG(rooftop_kilowatts) AS \"rooftop_kilowatts\"\n                FROM (\n                    select\n                        pv_forecast.forecast_from_date, pv_forecast.kilowatts*pv_rooftop.power AS rooftop_kilowatts, ROW_NUMBER() OVER (PARTITION BY forecast_from_date ORDER BY request_date DESC) AS rn\n                    from\n                        pv_forecast\n                    JOIN pv_rooftop ON pv_rooftop.id = pv_rooftop_id\n                    where\n                        forecast_from_date >= DATE(NOW()) AND forecast_to_date < DATE(NOW()) + INTERVAL 1 DAY\n                        and pv_rooftop_id = 2\n                    order by forecast_from_date, request_date\n                ) t1\n                WHERE\n                    rn = 1\n                GROUP BY time\n            ) t2\n        ),\n        pv_real_power AS (\n            SELECT time*(30*60)*1000 AS time, pv_ppv FROM (\n                SELECT\n                    FLOOR(UNIX_TIMESTAMP(log_date) / (30*60)) as time,\n                    AVG((pv_ppv)/1000) AS pv_ppv\n                FROM pv_inverter_record\n                WHERE\n                    log_date >= DATE(NOW()) AND log_date < DATE(NOW()) + INTERVAL 1 DAY\n                GROUP BY time\n            ) t1\n        )\n\n        SELECT\n            pv_forecast_ranked_string1.time,\n            pv_forecast_ranked_string1.rooftop_kilowatts + pv_forecast_ranked_string2.rooftop_kilowatts AS \"forecast\",\n            pv_real_power.pv_ppv AS \"real\"\n        FROM\n            pv_forecast_ranked_string1\n        JOIN pv_forecast_ranked_string2 ON pv_forecast_ranked_string2.time = pv_forecast_ranked_string1.time\n        LEFT JOIN pv_real_power ON pv_real_power.time = pv_forecast_ranked_string1.time\n    `,\n    values: [],\n});\n//node.warn(q);\n//node.warn(resMsgRows);\n\nmsg.payload = resMsgRows.payload;\nreturn msg;\n",
  4700.         "outputs": 1,
  4701.         "noerr": 0,
  4702.         "initialize": "",
  4703.         "finalize": "",
  4704.         "libs": [],
  4705.         "x": 634.1999893188477,
  4706.         "y": 1191.4000930786133,
  4707.         "wires": [
  4708.             [
  4709.                 "b401086f285896a2",
  4710.                 "ae6cc39307a3438d"
  4711.             ]
  4712.         ]
  4713.     },
  4714.     {
  4715.         "id": "b401086f285896a2",
  4716.         "type": "debug",
  4717.         "z": "303c9862.ddfff8",
  4718.         "name": "debug 32",
  4719.         "active": false,
  4720.         "tosidebar": true,
  4721.         "console": false,
  4722.         "tostatus": false,
  4723.         "complete": "false",
  4724.         "statusVal": "",
  4725.         "statusType": "auto",
  4726.         "x": 820.200080871582,
  4727.         "y": 1130.399850845337,
  4728.         "wires": []
  4729.     },
  4730.     {
  4731.         "id": "ae6cc39307a3438d",
  4732.         "type": "actionflows",
  4733.         "z": "303c9862.ddfff8",
  4734.         "info": "Describe your action API here.",
  4735.         "untilproptype": "num",
  4736.         "proptype": "msg",
  4737.         "name": "core.dashboard.graphize",
  4738.         "prop": "loop",
  4739.         "untilprop": 0,
  4740.         "until": "gt",
  4741.         "loop": "none",
  4742.         "scope": "global",
  4743.         "perf": false,
  4744.         "seq": false,
  4745.         "x": 865.200080871582,
  4746.         "y": 1182.3998527526855,
  4747.         "wires": [
  4748.             [
  4749.                 "4a1bea8e774f28cb"
  4750.             ]
  4751.         ]
  4752.     },
  4753.     {
  4754.         "id": "4a1bea8e774f28cb",
  4755.         "type": "ui_chart",
  4756.         "z": "303c9862.ddfff8",
  4757.         "name": "",
  4758.         "group": "d9bbfe4e.3d039",
  4759.         "order": 8,
  4760.         "width": 0,
  4761.         "height": 0,
  4762.         "label": "Power Today (kW)",
  4763.         "chartType": "line",
  4764.         "legend": "true",
  4765.         "xformat": "HH:mm",
  4766.         "interpolate": "linear",
  4767.         "nodata": "",
  4768.         "dot": false,
  4769.         "ymin": "",
  4770.         "ymax": "",
  4771.         "removeOlder": "24",
  4772.         "removeOlderPoints": "",
  4773.         "removeOlderUnit": "3600",
  4774.         "cutout": 0,
  4775.         "useOneColor": false,
  4776.         "useUTC": false,
  4777.         "colors": [
  4778.             "#6fb8ec",
  4779.             "#e6bf00",
  4780.             "#ff7f0e",
  4781.             "#2ca02c",
  4782.             "#98df8a",
  4783.             "#d62728",
  4784.             "#ff9896",
  4785.             "#9467bd",
  4786.             "#c5b0d5"
  4787.         ],
  4788.         "outputs": 1,
  4789.         "useDifferentColor": false,
  4790.         "className": "",
  4791.         "x": 1117.2000427246094,
  4792.         "y": 1182.3999347686768,
  4793.         "wires": [
  4794.             []
  4795.         ]
  4796.     },
  4797.     {
  4798.         "id": "904a6ce028e46aca",
  4799.         "type": "ui_text",
  4800.         "z": "303c9862.ddfff8",
  4801.         "group": "802549dc0ec48149",
  4802.         "order": 10,
  4803.         "width": 0,
  4804.         "height": 0,
  4805.         "name": "",
  4806.         "label": "Balance",
  4807.         "format": "{{msg.payload.export-msg.payload.import}}kWh",
  4808.         "layout": "row-spread",
  4809.         "className": "",
  4810.         "x": 858.0000762939453,
  4811.         "y": 4901.999437093735,
  4812.         "wires": []
  4813.     },
  4814.     {
  4815.         "id": "84d3387c34952bee",
  4816.         "type": "inject",
  4817.         "z": "303c9862.ddfff8",
  4818.         "name": "",
  4819.         "props": [
  4820.             {
  4821.                 "p": "payload"
  4822.             },
  4823.             {
  4824.                 "p": "todayOffset",
  4825.                 "v": "0",
  4826.                 "vt": "str"
  4827.             }
  4828.         ],
  4829.         "repeat": "3600",
  4830.         "crontab": "",
  4831.         "once": true,
  4832.         "onceDelay": 0.1,
  4833.         "topic": "",
  4834.         "payload": "",
  4835.         "payloadType": "date",
  4836.         "x": 458.1999816894531,
  4837.         "y": 4944.19970703125,
  4838.         "wires": [
  4839.             [
  4840.                 "9bdbee1ef29576f4"
  4841.             ]
  4842.         ]
  4843.     },
  4844.     {
  4845.         "id": "9bdbee1ef29576f4",
  4846.         "type": "function",
  4847.         "z": "303c9862.ddfff8",
  4848.         "name": "",
  4849.         "func": "const af = global.get('actionflows');\n\nconst BILLING_PERIOD_DATE_FROM = flow.get('billing_period_date_from');\n\n// zjistime zda-li je den zuctovani (resp. predchozi den) v budoucnosti\n// pokud je den v budoucnosti (napr. dnes je 1.9.2023 a ten den je 31.12.2023)\n// tak vezmeme z minulosti 2.9.2022-31.12.2022\nconst [day, month] = BILLING_PERIOD_DATE_FROM.split('.').map(Number);\nconst today = new Date();\nconst billingPeriodEndDate = new Date(today.getFullYear(), month - 1, day - 1);\n// opravit, aby to bylo tento rok, kdyby to nahodou preteklo do predchoziho\nbillingPeriodEndDate.setFullYear(today.getFullYear());\nconst billingPeriodEndDateInFuture = billingPeriodEndDate > today;\n\n\nconst resMsgEstimatedGridStats = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT SUM(imported) AS imported_kwh, SUM(exported) AS exported_kwh\n        FROM pv_daily_stats\n        WHERE\n            log_date >= DATE(NOW()) + INTERVAL 1 DAY - INTERVAL 1 YEAR\n            AND\n            log_date <= STR_TO_DATE('${billingPeriodEndDate.getDate()}.${billingPeriodEndDate.getMonth() + 1}.${billingPeriodEndDateInFuture ? billingPeriodEndDate.getFullYear() - 1 : billingPeriodEndDate.getFullYear()}', '%d.%m.%Y')\n    `,\n    values: [],\n});\n// node.warn(q);\nnode.warn(resMsgEstimatedGridStats);\n\nmsg.payload = {\n    import: Math.round(resMsgEstimatedGridStats.payload[0].imported_kwh),\n    export: Math.round(resMsgEstimatedGridStats.payload[0].exported_kwh),\n}\n\nflow.set('estimated_rest_billing_period_imported_kwh', msg.payload.import);\nflow.set('estimated_rest_billing_period_exported_kwh', msg.payload.export);\n\nreturn msg;\n",
  4850.         "outputs": 1,
  4851.         "noerr": 0,
  4852.         "initialize": "",
  4853.         "finalize": "",
  4854.         "libs": [],
  4855.         "x": 647.1999893188477,
  4856.         "y": 4944.199775695801,
  4857.         "wires": [
  4858.             [
  4859.                 "49535a063349de0f"
  4860.             ]
  4861.         ]
  4862.     },
  4863.     {
  4864.         "id": "49535a063349de0f",
  4865.         "type": "ui_text",
  4866.         "z": "303c9862.ddfff8",
  4867.         "group": "802549dc0ec48149",
  4868.         "order": 8,
  4869.         "width": 0,
  4870.         "height": 0,
  4871.         "name": "",
  4872.         "label": "Estimated I/E Rest Of Billing Period",
  4873.         "format": "{{msg.payload.import}}/{{msg.payload.export}}kWh",
  4874.         "layout": "row-spread",
  4875.         "className": "",
  4876.         "x": 939.1999893188477,
  4877.         "y": 4953.199775695801,
  4878.         "wires": []
  4879.     },
  4880.     {
  4881.         "id": "2187d4f8085d06b7",
  4882.         "type": "function",
  4883.         "z": "303c9862.ddfff8",
  4884.         "name": "",
  4885.         "func": "if (flow.get('billing_period_imported_kwh') && flow.get('estimated_rest_billing_period_imported_kwh')) {\n\n    msg.payload = {\n        export: flow.get('billing_period_exported_kwh') + flow.get('estimated_rest_billing_period_exported_kwh'),\n        import: flow.get('billing_period_imported_kwh') + flow.get('estimated_rest_billing_period_imported_kwh'),\n    }\n\n    return msg;\n}",
  4886.         "outputs": 1,
  4887.         "noerr": 0,
  4888.         "initialize": "",
  4889.         "finalize": "",
  4890.         "libs": [],
  4891.         "x": 1411.2000484466553,
  4892.         "y": 4821.199534416199,
  4893.         "wires": [
  4894.             [
  4895.                 "c316b5bc0610863e"
  4896.             ]
  4897.         ]
  4898.     },
  4899.     {
  4900.         "id": "c316b5bc0610863e",
  4901.         "type": "ui_text",
  4902.         "z": "303c9862.ddfff8",
  4903.         "group": "802549dc0ec48149",
  4904.         "order": 10,
  4905.         "width": 0,
  4906.         "height": 0,
  4907.         "name": "",
  4908.         "label": "Estimated Final Balance",
  4909.         "format": "{{msg.payload.export-msg.payload.import}}kWh",
  4910.         "layout": "row-spread",
  4911.         "className": "",
  4912.         "x": 1608.533302307129,
  4913.         "y": 4820.466135978699,
  4914.         "wires": []
  4915.     },
  4916.     {
  4917.         "id": "bb1a4f61dac0bb33",
  4918.         "type": "inject",
  4919.         "z": "303c9862.ddfff8",
  4920.         "name": "",
  4921.         "props": [
  4922.             {
  4923.                 "p": "payload"
  4924.             },
  4925.             {
  4926.                 "p": "todayOffset",
  4927.                 "v": "0",
  4928.                 "vt": "str"
  4929.             }
  4930.         ],
  4931.         "repeat": "3600",
  4932.         "crontab": "",
  4933.         "once": true,
  4934.         "onceDelay": 0.1,
  4935.         "topic": "",
  4936.         "payload": "",
  4937.         "payloadType": "date",
  4938.         "x": 1259.1999435424805,
  4939.         "y": 4823.199638366699,
  4940.         "wires": [
  4941.             [
  4942.                 "2187d4f8085d06b7"
  4943.             ]
  4944.         ]
  4945.     },
  4946.     {
  4947.         "id": "ce47cc5f7bbcaeed",
  4948.         "type": "function",
  4949.         "z": "303c9862.ddfff8",
  4950.         "name": "save pv_daily_stats",
  4951.         "func": "const af = global.get('actionflows');\n\n// ulozit do db\nlet resMsgStats = await af.invoke('core.db.query', {\n    database: 'smarthouse',\n    query: `\n        SELECT 1\n        FROM pv_daily_stats\n\t\tWHERE\n\t\tDATE(NOW() - INTERVAL ${msg.todayOffset} DAY) = DATE(log_date)\n    `,\n    values: [],\n});\n\nif (resMsgStats.payload.length == 0) {\n    resMsgStats = await af.invoke('core.db.query', {\n        database: 'smarthouse',\n        query: `\n            INSERT INTO pv_daily_stats SET log_date = DATE(NOW() - INTERVAL ${msg.todayOffset} DAY)\n        `\n    });\n}\n\nfor (const [name, value] of Object.entries(msg.dailyStats)) {\n    // node.warn(`${name} = ${value}`);\n    resMsgStats = await af.invoke('core.db.query', {\n        database: 'smarthouse',\n        query: `\n        UPDATE pv_daily_stats SET ${name} = ? WHERE log_date = DATE(NOW() - INTERVAL ${msg.todayOffset} DAY)\n    `,\n        values: [value],\n    });\n}\n\n// node.warn(`Daily stats saved. todayOffset: ${msg.todayOffset}`);",
  4952.         "outputs": 1,
  4953.         "noerr": 0,
  4954.         "initialize": "",
  4955.         "finalize": "",
  4956.         "libs": [],
  4957.         "x": 1488.200050354004,
  4958.         "y": 4422.19952917099,
  4959.         "wires": [
  4960.             []
  4961.         ]
  4962.     },
  4963.     {
  4964.         "id": "32d82afdd0473074",
  4965.         "type": "inject",
  4966.         "z": "303c9862.ddfff8",
  4967.         "d": true,
  4968.         "name": "",
  4969.         "props": [
  4970.             {
  4971.                 "p": "payload"
  4972.             },
  4973.             {
  4974.                 "p": "topic",
  4975.                 "vt": "str"
  4976.             }
  4977.         ],
  4978.         "repeat": "",
  4979.         "crontab": "",
  4980.         "once": false,
  4981.         "onceDelay": 0.1,
  4982.         "topic": "",
  4983.         "payload": "",
  4984.         "payloadType": "date",
  4985.         "x": 1343.1999492645264,
  4986.         "y": 4361.19970703125,
  4987.         "wires": [
  4988.             [
  4989.                 "2a47ca02c3933ec6"
  4990.             ]
  4991.         ]
  4992.     },
  4993.     {
  4994.         "id": "2a47ca02c3933ec6",
  4995.         "type": "function",
  4996.         "z": "303c9862.ddfff8",
  4997.         "name": "recalc pv_daily_stats",
  4998.         "func": "async function sleep(ms) {\n    return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfor (let todayOffset = 365; todayOffset <= 365+4*31; todayOffset++) {\n    node.send({\n        todayOffset,\n        payload: {},\n    });\n\n    await sleep(1000);\n}",
  4999.         "outputs": 1,
  5000.         "noerr": 0,
  5001.         "initialize": "",
  5002.         "finalize": "",
  5003.         "libs": [],
  5004.         "x": 1530.200050354004,
  5005.         "y": 4356.1995277404785,
  5006.         "wires": [
  5007.             [
  5008.                 "d96eb2fb252d9ecc",
  5009.                 "478f152e86f5bbaa",
  5010.                 "df2ff145fbb01179"
  5011.             ]
  5012.         ]
  5013.     },
  5014.     {
  5015.         "id": "23fce76ac4f7722e",
  5016.         "type": "comment",
  5017.         "z": "303c9862.ddfff8",
  5018.         "name": "Init",
  5019.         "info": "",
  5020.         "x": 270.1999816894531,
  5021.         "y": 264.1999702453613,
  5022.         "wires": []
  5023.     },
  5024.     {
  5025.         "id": "2e9be9117e4f9ba9",
  5026.         "type": "comment",
  5027.         "z": "303c9862.ddfff8",
  5028.         "name": "Battery SOC Prediction",
  5029.         "info": "",
  5030.         "x": 438.19996643066406,
  5031.         "y": 198.20000076293945,
  5032.         "wires": []
  5033.     },
  5034.     {
  5035.         "id": "abdb064dbd899fb8",
  5036.         "type": "inject",
  5037.         "z": "303c9862.ddfff8",
  5038.         "name": "",
  5039.         "props": [
  5040.             {
  5041.                 "p": "payload"
  5042.             },
  5043.             {
  5044.                 "p": "topic",
  5045.                 "vt": "str"
  5046.             }
  5047.         ],
  5048.         "repeat": "",
  5049.         "crontab": "",
  5050.         "once": true,
  5051.         "onceDelay": 0.1,
  5052.         "topic": "",
  5053.         "payload": "",
  5054.         "payloadType": "date",
  5055.         "x": 661.0001449584961,
  5056.         "y": 206.00000381469727,
  5057.         "wires": [
  5058.             [
  5059.                 "1bacfdafa261f61d"
  5060.             ]
  5061.         ]
  5062.     },
  5063.     {
  5064.         "id": "1bacfdafa261f61d",
  5065.         "type": "function",
  5066.         "z": "303c9862.ddfff8",
  5067.         "name": "Battery SOC Prediction JS Module",
  5068.         "func": "flow.set('battery_soc_prediction_module_path', '/home/gibo/dev/misc/pv/battery_soc_prediction/engine');\nreturn msg;",
  5069.         "outputs": 1,
  5070.         "noerr": 0,
  5071.         "initialize": "",
  5072.         "finalize": "",
  5073.         "libs": [],
  5074.         "x": 923.0001182556152,
  5075.         "y": 213.00000381469727,
  5076.         "wires": [
  5077.             []
  5078.         ]
  5079.     },
  5080.     {
  5081.         "id": "76a2023f.8c254c",
  5082.         "type": "MySQLdatabase",
  5083.         "name": "",
  5084.         "host": "mysql.smarthouse.vp.ip.kyrian.cz",
  5085.         "port": "3306",
  5086.         "db": "smarthouse",
  5087.         "tz": "",
  5088.         "charset": ""
  5089.     },
  5090.     {
  5091.         "id": "312b72db.d7629e",
  5092.         "type": "ui_group",
  5093.         "name": "Power",
  5094.         "tab": "7de7f9b4.9294a8",
  5095.         "order": 5,
  5096.         "disp": true,
  5097.         "width": "6",
  5098.         "collapse": false
  5099.     },
  5100.     {
  5101.         "id": "b55f633.0a700a",
  5102.         "type": "ui_group",
  5103.         "name": "Battery",
  5104.         "tab": "7de7f9b4.9294a8",
  5105.         "order": 6,
  5106.         "disp": true,
  5107.         "width": "6",
  5108.         "collapse": false
  5109.     },
  5110.     {
  5111.         "id": "d9bbfe4e.3d039",
  5112.         "type": "ui_group",
  5113.         "name": "Forecast",
  5114.         "tab": "7de7f9b4.9294a8",
  5115.         "order": 3,
  5116.         "disp": true,
  5117.         "width": "6",
  5118.         "collapse": false
  5119.     },
  5120.     {
  5121.         "id": "819686be.5e6a58",
  5122.         "type": "ui_group",
  5123.         "name": "Charts",
  5124.         "tab": "7de7f9b4.9294a8",
  5125.         "order": 4,
  5126.         "disp": true,
  5127.         "width": "6",
  5128.         "collapse": false
  5129.     },
  5130.     {
  5131.         "id": "d1547785.b0eb48",
  5132.         "type": "ui_group",
  5133.         "name": "Phases Load",
  5134.         "tab": "7de7f9b4.9294a8",
  5135.         "order": 7,
  5136.         "disp": true,
  5137.         "width": "6",
  5138.         "collapse": false
  5139.     },
  5140.     {
  5141.         "id": "e7d46503.f3f4b8",
  5142.         "type": "ui_group",
  5143.         "name": "Power Details",
  5144.         "tab": "7de7f9b4.9294a8",
  5145.         "order": 10,
  5146.         "disp": true,
  5147.         "width": "6",
  5148.         "collapse": false
  5149.     },
  5150.     {
  5151.         "id": "d1c39829.3ee0e8",
  5152.         "type": "ui_group",
  5153.         "name": "Battery SOC Prediction",
  5154.         "tab": "7de7f9b4.9294a8",
  5155.         "order": 9,
  5156.         "disp": true,
  5157.         "width": "6",
  5158.         "collapse": false
  5159.     },
  5160.     {
  5161.         "id": "36ca1673.3376ea",
  5162.         "type": "jsonrpc-client",
  5163.         "name": "smarthouse-core",
  5164.         "host": "vs-smarthouse",
  5165.         "port": "2004",
  5166.         "connection": "http"
  5167.     },
  5168.     {
  5169.         "id": "802549dc0ec48149",
  5170.         "type": "ui_group",
  5171.         "name": "Energy",
  5172.         "tab": "7de7f9b4.9294a8",
  5173.         "order": 8,
  5174.         "disp": true,
  5175.         "width": "6",
  5176.         "collapse": false
  5177.     },
  5178.     {
  5179.         "id": "9a78c351d1914ac8",
  5180.         "type": "ui_group",
  5181.         "name": "Status",
  5182.         "tab": "7de7f9b4.9294a8",
  5183.         "order": 1,
  5184.         "disp": true,
  5185.         "width": "6",
  5186.         "collapse": false
  5187.     },
  5188.     {
  5189.         "id": "cf60b551c034e51c",
  5190.         "type": "ui_group",
  5191.         "name": "Backup / On-Grid Mode",
  5192.         "tab": "7de7f9b4.9294a8",
  5193.         "order": 2,
  5194.         "disp": true,
  5195.         "width": "6",
  5196.         "collapse": false,
  5197.         "className": ""
  5198.     },
  5199.     {
  5200.         "id": "347876e0a6d5c1b0",
  5201.         "type": "position-config",
  5202.         "name": "",
  5203.         "isValide": "true",
  5204.         "angleType": "deg",
  5205.         "timeZoneOffset": 99,
  5206.         "timeZoneDST": 0,
  5207.         "stateTimeFormat": "3",
  5208.         "stateDateFormat": "12",
  5209.         "contextStore": ""
  5210.     },
  5211.     {
  5212.         "id": "45ecb2bfdd21bc90",
  5213.         "type": "ui_group",
  5214.         "name": "Sun",
  5215.         "tab": "7de7f9b4.9294a8",
  5216.         "order": 11,
  5217.         "disp": true,
  5218.         "width": "6",
  5219.         "collapse": false,
  5220.         "className": ""
  5221.     },
  5222.     {
  5223.         "id": "7de7f9b4.9294a8",
  5224.         "type": "ui_tab",
  5225.         "name": "FVE",
  5226.         "icon": "dashboard",
  5227.         "order": 17,
  5228.         "disabled": false,
  5229.         "hidden": false
  5230.     }
  5231. ]
Advertisement
Add Comment
Please, Sign In to add comment