Phasing out the Node-RED Dynamic ESS implementation

Can I email the flow to this address then ? victron@av-e.nl

I have a paid version supergrok, its still rubbish haha

1 Like

Yes, best is to zip it with a password let say ā€˜nodered’

Or via X account assuming you have one. Does chat, talk and files I saw mentioned this week. @kevdav100

I’m past that initial learning curve, would much appreciate to be granted a look at some example code, no expectations of direct use ability, just for learning. @BartChampagne

Just sent the email, have fun, and let us know if any good for your needs, will obviously need some major reworks for your market.

Thanks, I have put mine above: Phasing out the Node-RED Dynamic ESS implementation - #8 by UpCycleElectric
Same password
…
Hybrid ā€˜keep batteries charged’ just activated, added screenshots above.

You like spaghetti a lot @kevdav100 ?:grinning_face::grinning_face::grinning_face:
No worries, I’ll figure it out. The provided adaptation steps seem common sense and readily available. I replied to your E-mail, pls confirm receipt.

Gemini likes spaghetti :).. I will get it to reorganise the flows at some point to tidy them up, my main goal now is to migrate the system onto the PI ..

Yes recieved your replies, will look at them later when I have more time

Cheers

1 Like

Don’t put too much expectations on my code :wink:
Currently I’m swamped with my construction project and telecom work (a big network overhaul that’s turning out to be a LOT more complex than I anticipated) and with the release of Venus OS 3.60 around the corner I’d like to clean up some of my kludgy code.
Since the DESS code and functions have heavily changed from v3.55 to v3.60 I’ll need to completely rethink & redo some of my logic.
And on top of all there’s something else brewing that I can’t disclose yet, simply because I haven’t the foggiest idea about how it might turn out.
I either need 36 hour days or a couple months off to catch up with my backlog.
(The latter didn’t really work in 2020 so damnit)

Make that 48

1 Like

@dfaber I think the API way of feeding DESS still requires a lot of attention. One, there is undocumented way of updating the schedule, and sent via API schedule is working bad → [BUG] DESS - VRM is displaying wrong values after API update

Any news about this? I created a work around but things get complicated quickly, see also here: UpCycle Hybrid DESS Trade

No news on that yet. Been a bit busy with other priorities the last couple of days.

I think by now I’ve tried everything I could imagine to ā€˜emulate’ this in VRM DESS (using Node-RED) and came up with a reasonably working solution. But it is a dearly missed option that makes VRM DESS pretty much dysfunctional for a DESS Trade only system.

And the preparation seems there in VRM-API:

By the way, there is a strange bug in your ā€˜sum inputs’ subflow, sometimes it is off by exactly +1.0
I ended up using a simplified function based on it.

The news is that we will phase the b_goal_soc and b_goal_hour parameters completely.

I hope you mean ā€˜phase in’ not ā€˜phase out’ with that. For a practical example of the use case see also this topic with data from today: Another Strange DESS behaviour - #13 by UpCycleElectric

If not, I would be very interested in an evaluation of my workaround and some guidance on how Victron would propose to solve the ā€˜issue’ that it represents. (or re-evaluate the decision not to bring this feature to VRM DESS).

Maybe you plan to enable the scheduled charge levels to work in combination with DESS? And I’d love to learn more about (or even participate in) the efforts to incorporate ā€˜predictive pricing’ beyond the ā€˜known prices’ into the schedule forecast data. A pricing forecast window longer than the current 24 to 48 hours, even if less accurate beyond the actual known pricing window, could also solve a lot of the missed trading opportunity issues as it could prevent the rather stubborn behaviour to target minimum SoC at end of the same day (or next day after new prices roll in). Awell, many ways to skin a cat I guess, looking forward to the new developments, it is still very early in the DESS development space.

I also hope the b_goal_soc and b_goal_hour will stay. I would work great to implement my goal to make sure to have a full battery after sunset.

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

That is very interesting, thanks for sharing.

I have a singular question:

Does this provide a port of Node-RED DESS’s ability to set b_goal_SOC @ b_goal_hour?

Not having this ability (to force the DESS scheduler to target a preset SOC at a preset time) ported to the VRM-API DESS implementation is the only roadblock for us to switch over.

Yes, the VRM API node has the same options and look-and-feel as the one that is being planned for Deprecation. It contains a subset of the ā€œold oneā€ (retreive the data from VRM) but does not set flow.dess nor outputs the next 4 hour schedules. That’s why I added those 2 nodes in the flow.

Second difference is it is now managed by Victron / venus-OS updates instead of being a custom integration.

Screenshot of the last options of the VRM API node with the Dynamic ESS api-type: