Phasing out the Node-RED Dynamic ESS implementation

This is probably a drop-in-replacement for the Node-RED implementation of Dynamic ess using the VRM-API node and the “original flow”.

I used some code from dynamic-ess/src/nodes/victron-dynamic-ess.js at main · victronenergy/dynamic-ess · GitHub for this.

It is probably not yet 15-minutes compatible but I think this will be easy to add in the near future using the dess.is_half_hour_schedule variable (or similar).

[
    {
        "id": "ddb754a848a5437c",
        "type": "group",
        "z": "d457e88372dcd44a",
        "name": "Dynamic ESS",
        "style": {
            "label": true
        },
        "nodes": [
            "1857e6dc9a194fec",
            "7049b7140944117e",
            "76a5b63506d3a697",
            "8533359282a12955",
            "756103b9698a27ff",
            "d81811500e0ae9f8",
            "484523849a6134a7",
            "780ff00a1393a6fe",
            "70fcd76ccd733faf"
        ],
        "x": 974,
        "y": 264,
        "w": 432,
        "h": 317
    },
    {
        "id": "1857e6dc9a194fec",
        "type": "link out",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "schedule 0 - current hour",
        "mode": "link",
        "links": [
            "da28729197a836c4",
            "36f3c65014ac2767"
        ],
        "x": 1305,
        "y": 420,
        "wires": []
    },
    {
        "id": "7049b7140944117e",
        "type": "link out",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "schedule 1 - current hour +1",
        "mode": "link",
        "links": [
            "9072b4ed90c3d650"
        ],
        "x": 1305,
        "y": 460,
        "wires": []
    },
    {
        "id": "76a5b63506d3a697",
        "type": "link out",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "schedule 2 - current hour +2",
        "mode": "link",
        "links": [
            "7677870734c617e4"
        ],
        "x": 1305,
        "y": 500,
        "wires": []
    },
    {
        "id": "8533359282a12955",
        "type": "link out",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "schedule 3 - current hour +3",
        "mode": "link",
        "links": [
            "163da36509915168"
        ],
        "x": 1305,
        "y": 540,
        "wires": []
    },
    {
        "id": "756103b9698a27ff",
        "type": "inject",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "300",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 1090,
        "y": 320,
        "wires": [
            [
                "70fcd76ccd733faf"
            ]
        ]
    },
    {
        "id": "d81811500e0ae9f8",
        "type": "function",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "VRM DESS compatibility",
        "func": "let dess = msg.payload\n\n/**\nconst currentDateTime = new Date()\ncurrentDateTime.setMinutes(0, 0, 0)\n\nconst unixTimestamp = Math.floor(currentDateTime.getTime() / 1000)\n\n\nlet currentHour = currentDateTime.getHours()\nif (currentHour > Object.keys(dess.schedule).length) {\n    currentHour -= 24\n}\n**/\nconst output = []\nif (dess.output) {\n    const currentDateTime = new Date()\n    currentDateTime.setMinutes(0, 0, 0)\n    const unixTimestamp = Math.floor(currentDateTime.getTime() / 1000)\n    const currentHour = currentDateTime.getHours()\n    for (let schedule = 0; schedule <= 3; schedule++) {\n      let schedulePick = currentHour + schedule\n      if (dess.is_half_hour_schedule) {\n        schedulePick = (currentHour * 2) + schedule\n        if (schedulePick > Object.keys(dess.output.SOC).length) {\n          schedulePick -= 48\n        }\n        if (currentHour === 0 && Object.keys(dess.output.SOC).length > 48) {\n          schedulePick += 48\n        }\n      } else {\n        if (schedulePick > Object.keys(dess.output.SOC).length) {\n          schedulePick -= 24\n        }\n        if (currentHour === 0 && Object.keys(dess.output.SOC).length > 24) {\n          schedulePick += 24\n        }\n      }\n      output.push({\n        topic: `Schedule ${schedule}`,\n        soc: Number((dess.output.SOC[schedulePick])),\n        feed_in: dess.options.feed_in_possible ? 1 : 0,\n        duration: dess.is_half_hour_schedule ? 1800 : 3600,\n        start: unixTimestamp + (schedule * (dess.is_half_hour_schedule ? 1800 : 3600)),\n        restrictions: Number((dess.output.restrictions[schedulePick] || 0)),\n        strategy: dess.output.coping_strategy[schedulePick]\n      })\n    }\n}\n\nreturn output\n\n/**\nreturn [\n    {\n        soc: dess.output.SOC[currentHour],\n        feed_in: dess.output.feed_in[currentHour],\n        start: unixTimestamp,\n        duration: 3600,\n        restrictions: dess.output.restrictions[currentHour],\n        strategy: dess.output.coping_strategy[currentHour],\n    },\n    {\n        soc: dess.output.SOC[currentHour+1],\n        feed_in: dess.output.feed_in[currentHour+1],\n        start: new Date(dess.output.timestamps[currentHour+1]),\n        duration: 3600,\n        restrictions: dess.output.restrictions[currentHour+1],\n        strategy: dess.output.coping_strategy[currentHour+1],\n    },\n    {\n        soc: dess.output.SOC[currentHour+2],\n        feed_in: dess.output.feed_in[currentHour+2],\n        start: new Date(dess.output.timestamps[currentHour+2]),\n        duration: 3600,\n        restrictions: dess.output.restrictions[currentHour+2],\n        strategy: dess.output.coping_strategy[currentHour+2],\n    },\n    {\n        soc: dess.output.SOC[currentHour+3],\n        feed_in: dess.output.feed_in[currentHour+3],\n        start: new Date(dess.output.timestamps[currentHour+3]),\n        duration: 3600,\n        restrictions: dess.output.restrictions[currentHour+3],\n        strategy: dess.output.coping_strategy[currentHour+3],\n    }\n];\n**/",
        "outputs": 4,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1110,
        "y": 520,
        "wires": [
            [
                "1857e6dc9a194fec"
            ],
            [
                "7049b7140944117e"
            ],
            [
                "76a5b63506d3a697"
            ],
            [
                "8533359282a12955"
            ]
        ]
    },
    {
        "id": "484523849a6134a7",
        "type": "change",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "dess",
                "pt": "flow",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1070,
        "y": 460,
        "wires": [
            [
                "d81811500e0ae9f8"
            ]
        ]
    },
    {
        "id": "780ff00a1393a6fe",
        "type": "victron-dynamic-ess",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "name": "DEPRECATED",
        "vrm_id": "",
        "vrmtoken": "",
        "country": "",
        "contract_buy": "",
        "contract_sell": "",
        "b_max": 10,
        "fb_max": 1.1,
        "tb_max": 1.2,
        "fg_max": 1,
        "tg_max": 1,
        "b_cycle_cost": 0,
        "buy_price_formula": "p",
        "sell_price_formula": "p",
        "green_mode_on": true,
        "b_goal_hour": 0,
        "b_goal_SOC": 0,
        "feed_in_possible": true,
        "feed_in_control_on": true,
        "verbose": false,
        "x": 1300,
        "y": 320,
        "wires": [
            [
                "1857e6dc9a194fec"
            ],
            [
                "7049b7140944117e"
            ],
            [
                "76a5b63506d3a697"
            ],
            [
                "8533359282a12955"
            ]
        ]
    },
    {
        "id": "70fcd76ccd733faf",
        "type": "vrm-api",
        "z": "d457e88372dcd44a",
        "g": "ddb754a848a5437c",
        "vrm": "",
        "name": "",
        "api_type": "dynamic-ess",
        "idUser": "",
        "users": "",
        "idSite": "",
        "installations": "",
        "attribute": "",
        "stats_interval": "",
        "show_instance": false,
        "stats_start": "",
        "stats_end": "",
        "use_utc": false,
        "gps_start": "",
        "gps_end": "",
        "widgets": "",
        "instance": "",
        "vrm_id": "",
        "country": "",
        "b_max": "",
        "tb_max": "",
        "fb_max": "",
        "tg_max": "",
        "fg_max": "",
        "b_cycle_cost": "",
        "buy_price_formula": "",
        "sell_price_formula": "",
        "green_mode_on": "",
        "feed_in_possible": "",
        "feed_in_control_on": "",
        "b_goal_hour": "",
        "b_goal_SOC": "",
        "store_in_global_context": false,
        "verbose": false,
        "x": 1080,
        "y": 400,
        "wires": [
            [
                "484523849a6134a7"
            ]
        ]
    }
]