Thanks Bart, I was still using custom inputs and just reconfigured everything based on your post. I am using HomeWizard meters and sockets to measure my PV. It currently looks like this:
[
{
"id": "c638519ca3f162f8",
"type": "group",
"z": "b51fc0718e9c7f9d",
"style": {
"stroke": "#999999",
"stroke-opacity": "1",
"fill": "none",
"fill-opacity": "1",
"label": true,
"label-position": "nw",
"color": "#a4a4a4"
},
"nodes": [
"c942bad4ad7105fd",
"8116ebb3ac362090",
"8f65547a34d2b35f",
"8519a23a0f847c60",
"a01c2d5855f30b8d",
"a008613031afeb37",
"811096c848896695",
"1aa0d1b15a78f3be",
"2ee0b636cdba77fa"
],
"x": 54,
"y": 2139,
"w": 1272,
"h": 202
},
{
"id": "c942bad4ad7105fd",
"type": "victron-virtual",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "PV Veranda",
"outputs": 1,
"device": "pvinverter",
"default_values": false,
"enable_s2support": false,
"battery_capacity": 25,
"include_battery_temperature": false,
"battery_voltage_custom": "",
"include_engine_hours": false,
"include_starter_voltage": false,
"include_history_energy": false,
"grid_nrofphases": 1,
"include_motor_temp": false,
"include_controller_temp": false,
"include_coolant_temp": false,
"include_motor_rpm": true,
"include_motor_direction": true,
"position": "1",
"pvinverter_nrofphases": "3",
"fluid_type": 0,
"include_tank_battery": false,
"include_tank_temperature": false,
"tank_battery_voltage": 3.3,
"tank_capacity": 0.2,
"temperature_type": 2,
"include_humidity": false,
"include_pressure": false,
"include_temp_battery": false,
"temp_battery_voltage": 3.3,
"x": 1010,
"y": 2240,
"wires": [
[
"2ee0b636cdba77fa"
]
]
},
{
"id": "8116ebb3ac362090",
"type": "inject",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "",
"props": [
{
"p": "payload"
}
],
"repeat": "5",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 170,
"y": 2220,
"wires": [
[
"8f65547a34d2b35f"
]
]
},
{
"id": "8f65547a34d2b35f",
"type": "http request",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "PV - HomeWizard Measurements",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "http://192.168.88.170/api/v1/data",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 460,
"y": 2220,
"wires": [
[
"1aa0d1b15a78f3be"
]
]
},
{
"id": "8519a23a0f847c60",
"type": "inject",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "5",
"topic": "/Ac/MaxPower",
"payload": "4",
"payloadType": "num",
"x": 510,
"y": 2180,
"wires": [
[
"1aa0d1b15a78f3be"
]
]
},
{
"id": "a01c2d5855f30b8d",
"type": "http request",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "PV - HomeWizard Information",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "http://192.168.88.170/api",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 470,
"y": 2260,
"wires": [
[
"1aa0d1b15a78f3be"
]
]
},
{
"id": "a008613031afeb37",
"type": "inject",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 170,
"y": 2260,
"wires": [
[
"a01c2d5855f30b8d"
]
]
},
{
"id": "811096c848896695",
"type": "link in",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "Throttle",
"links": [
"7a1d5ae47e397e2e"
],
"x": 585,
"y": 2300,
"wires": [
[
"1aa0d1b15a78f3be"
]
]
},
{
"id": "1aa0d1b15a78f3be",
"type": "function",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "Parse & Route PV Data",
"func": "const data = msg.payload;\n\nlet payload = {};\nlet statusCode = null;\n\n// --- Throttling (optional): if you send msg.topic=\"throttling\" ---\nif (msg.topic === \"throttling\" && (data === true || data === false)) {\n context.set(\"isThrottling\", data);\n return null;\n}\nconst isThrottling = context.get(\"isThrottling\") === true;\nif (isThrottling) statusCode = 12;\n\n// --- One-time inject: /Ac/MaxPower ---\nif (msg.topic === \"/Ac/MaxPower\") {\n payload[\"/Ac/MaxPower\"] = Number(data);\n msg.payload = payload;\n return msg;\n}\n\n// --- Determine phase mode from product_type (when available) ---\n// HWE-KWH3 => 3-phase\n// HWE-KWH1, HWE-SKT, unknown => 1-phase\nlet phaseMode = null; // 1 or 3\n\nif (typeof data === \"object\" && data && data.product_type) {\n payload[\"/ProductName\"] = String(data.product_type);\n if (data.serial) payload[\"/Serial\"] = String(data.serial);\n\n if (data.product_type === \"HWE-KWH3\") phaseMode = 3;\n else phaseMode = 1; // includes HWE-KWH1, HWE-SKT, and any unknown types\n\n // Save mode so measurement messages can use it\n context.set(\"phaseMode\", phaseMode);\n\n msg.payload = payload;\n return msg;\n}\n\n// If no product_type in this msg, fall back to last known mode (default 1)\nphaseMode = context.get(\"phaseMode\") || 1;\n\n// --- Measurement handling ---\nif (typeof data === \"object\" && data && (\"wifi_ssid\" in data)) {\n\n if (phaseMode === 3) {\n // Voltage\n payload[\"/Ac/L1/Voltage\"] = Number(data.active_voltage_l1_v);\n payload[\"/Ac/L2/Voltage\"] = Number(data.active_voltage_l2_v);\n payload[\"/Ac/L3/Voltage\"] = Number(data.active_voltage_l3_v);\n\n // Current\n payload[\"/Ac/L1/Current\"] = Number(data.active_current_l1_a);\n payload[\"/Ac/L2/Current\"] = Number(data.active_current_l2_a);\n payload[\"/Ac/L3/Current\"] = Number(data.active_current_l3_a);\n\n // Power (negated for PV export display)\n payload[\"/Ac/L1/Power\"] = -1 * Number(data.active_power_l1_w);\n payload[\"/Ac/L2/Power\"] = -1 * Number(data.active_power_l2_w);\n payload[\"/Ac/L3/Power\"] = -1 * Number(data.active_power_l3_w);\n payload[\"/Ac/Power\"] = -1 * Number(data.active_power_w);\n\n // Energy\n if (\"total_power_export_kwh\" in data) {\n const total = Number(data.total_power_export_kwh);\n payload[\"/Ac/Energy/Forward\"] = total;\n\n const perPhase = total / 3;\n payload[\"/Ac/L1/Energy/Forward\"] = perPhase;\n payload[\"/Ac/L2/Energy/Forward\"] = perPhase;\n payload[\"/Ac/L3/Energy/Forward\"] = perPhase;\n }\n\n // StatusCode fallback (if not throttling)\n if (statusCode === null) {\n statusCode = Math.abs(Number(data.active_power_w)) > 0 ? 7 : 8;\n }\n\n } else {\n // 1-phase (L1 only)\n payload[\"/Ac/L1/Voltage\"] = Number(data.active_voltage_v);\n payload[\"/Ac/L1/Current\"] = Number(data.active_current_a);\n\n // Power (negated for PV export display)\n payload[\"/Ac/L1/Power\"] = -1 * Number(data.active_power_l1_w);\n payload[\"/Ac/Power\"] = -1 * Number(data.active_power_w);\n\n // Energy\n if (\"total_power_export_kwh\" in data) {\n const total = Number(data.total_power_export_kwh);\n payload[\"/Ac/Energy/Forward\"] = total;\n payload[\"/Ac/L1/Energy/Forward\"] = total;\n }\n\n // StatusCode fallback (if not throttling)\n if (statusCode === null) {\n statusCode = Math.abs(Number(data.active_power_w)) > 0 ? 7 : 8;\n }\n }\n\n if (statusCode !== null) payload[\"/StatusCode\"] = statusCode;\n\n msg.payload = payload;\n return msg;\n}\n\nreturn null;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 770,
"y": 2240,
"wires": [
[
"c942bad4ad7105fd"
]
]
},
{
"id": "2ee0b636cdba77fa",
"type": "debug",
"z": "b51fc0718e9c7f9d",
"g": "c638519ca3f162f8",
"name": "debug 12",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1220,
"y": 2240,
"wires": []
},
{
"id": "f26b4f5c6c83d546",
"type": "global-config",
"env": [],
"modules": {
"@victronenergy/node-red-contrib-victron": "1.6.56"
}
}
]
And this is what it looks like for an AC Load:
[
{
"id": "ac5c81226c76d6e8",
"type": "group",
"z": "b51fc0718e9c7f9d",
"style": {
"stroke": "#999999",
"stroke-opacity": "1",
"fill": "none",
"fill-opacity": "1",
"label": true,
"label-position": "nw",
"color": "#a4a4a4"
},
"nodes": [
"c5c969651cadb2e7",
"c016a63ac04337a5",
"30111398d9e167ce",
"0910ad649486a7a7",
"fdb30a6c6fbb023d",
"e59be2c46b7c12b9",
"5015e22747103914"
],
"x": 34,
"y": 1119,
"w": 1252,
"h": 142
},
{
"id": "c5c969651cadb2e7",
"type": "victron-virtual",
"z": "b51fc0718e9c7f9d",
"g": "ac5c81226c76d6e8",
"name": "Heatpump",
"outputs": 1,
"device": "acload",
"default_values": false,
"acload_nrofphases": 1,
"enable_s2support": false,
"battery_capacity": 25,
"include_battery_temperature": false,
"battery_voltage_preset": "24",
"battery_voltage_custom": 24,
"generator_type": "ac",
"generator_nrofphases": 1,
"include_engine_hours": false,
"include_starter_voltage": false,
"include_history_energy": false,
"grid_nrofphases": 1,
"include_motor_temp": false,
"include_controller_temp": false,
"include_coolant_temp": false,
"include_motor_rpm": true,
"include_motor_direction": true,
"position": 0,
"pvinverter_nrofphases": 1,
"switch_1_type": 1,
"switch_1_min": 0,
"switch_1_max": "",
"switch_1_initial": 0,
"switch_1_label": "",
"switch_1_unit": "",
"switch_1_step": 1,
"switch_1_customname": "",
"switch_1_group": "",
"switch_1_include_measurement": false,
"switch_1_rgb_color_wheel": false,
"switch_1_cct_wheel": false,
"switch_1_rgb_white_dimmer": false,
"fluid_type": 0,
"include_tank_battery": false,
"include_tank_temperature": false,
"tank_battery_voltage": 3.3,
"tank_capacity": 0.2,
"temperature_type": 2,
"include_humidity": false,
"include_pressure": false,
"include_temp_battery": false,
"temp_battery_voltage": 3.3,
"x": 970,
"y": 1180,
"wires": [
[
"5015e22747103914"
]
]
},
{
"id": "c016a63ac04337a5",
"type": "inject",
"z": "b51fc0718e9c7f9d",
"g": "ac5c81226c76d6e8",
"name": "",
"props": [
{
"p": "payload"
}
],
"repeat": "5",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 150,
"y": 1160,
"wires": [
[
"fdb30a6c6fbb023d"
]
]
},
{
"id": "30111398d9e167ce",
"type": "inject",
"z": "b51fc0718e9c7f9d",
"g": "ac5c81226c76d6e8",
"name": "",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 150,
"y": 1220,
"wires": [
[
"0910ad649486a7a7"
]
]
},
{
"id": "0910ad649486a7a7",
"type": "http request",
"z": "b51fc0718e9c7f9d",
"g": "ac5c81226c76d6e8",
"name": "AC - HomeWizard Information",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "http://192.168.88.59/api",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 390,
"y": 1220,
"wires": [
[
"e59be2c46b7c12b9"
]
]
},
{
"id": "fdb30a6c6fbb023d",
"type": "http request",
"z": "b51fc0718e9c7f9d",
"g": "ac5c81226c76d6e8",
"name": "AC - HomeWizard Measurements",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "http://192.168.88.59/api/v1/data",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 400,
"y": 1160,
"wires": [
[
"e59be2c46b7c12b9"
]
]
},
{
"id": "e59be2c46b7c12b9",
"type": "function",
"z": "b51fc0718e9c7f9d",
"g": "ac5c81226c76d6e8",
"name": "Parse & Route AC Data",
"func": "const data = msg.payload;\n\n// Load last-known state so we can merge /api + /api/v1/data updates\nconst state = flow.get(\"device_stats\") || {};\nlet payload = { ...state };\n\nlet changed = false;\n\n// --- Power Data Handling ---\nif (typeof data === \"object\" && data && (\"wifi_ssid\" in data)) {\n payload[\"/Ac/L1/Voltage\"] = data.active_voltage_v;\n payload[\"/Ac/L1/Current\"] = data.active_current_a;\n payload[\"/Ac/L1/Power\"] = Number(data.active_power_l1_w);\n payload[\"/Ac/L1/PowerFactor\"] = data.active_power_factor;\n payload[\"/Ac/L1/Energy/Forward\"] = data.total_power_import_kwh;\n payload[\"/Ac/L1/Energy/Reverse\"] = 0;\n\n payload[\"/Ac/Power\"] = Number(data.active_power_w);\n payload[\"/Ac/Energy/Forward\"] = data.total_power_import_kwh;\n payload[\"/Ac/Energy/Reverse\"] = 0;\n\n changed = true;\n}\n\n// --- Product Info Handling ---\nif (typeof data === \"object\" && data && (\"product_type\" in data) && (\"serial\" in data)) {\n payload[\"/ProductName\"] = data.product_type;\n payload[\"/Serial\"] = data.serial;\n\n changed = true;\n}\n\n// If neither block matched, don't send anything\nif (!changed) return null;\n\n// Save merged state\nflow.set(\"device_stats\", payload);\n\n// Output to victron-virtual\nmsg.payload = payload;\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 730,
"y": 1180,
"wires": [
[
"c5c969651cadb2e7"
]
]
},
{
"id": "5015e22747103914",
"type": "debug",
"z": "b51fc0718e9c7f9d",
"g": "ac5c81226c76d6e8",
"name": "debug 2",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1180,
"y": 1180,
"wires": []
},
{
"id": "bd417682b346c63b",
"type": "global-config",
"env": [],
"modules": {
"@victronenergy/node-red-contrib-victron": "1.6.56"
}
}
]