Initial commit: CODESYS tree, integration docs, Node-RED scripts.

PLC application sources (NVL, rooms, boiler), HA/Node-RED integration guide, and NVL patch utilities.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-26 21:21:56 +03:00
commit d98c44bfa3
27 changed files with 3844 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
(* === DECLARATION === *)
(* Water heater fb_boiler; coil mapped to NVL_Sender.boiler_status.relay_output (%QX5.4). *)
PROGRAM Boiler_PRG
VAR
heater : fb_boiler;
max_on_minutes : INT;
max_on_time : TIME;
END_VAR
(* === IMPLEMENTATION === *)
max_on_minutes := NVL_Receiver.boiler_cmd.max_on_time_minutes;
IF max_on_minutes <= 0 THEN
max_on_minutes := 480;
END_IF;
max_on_time := T#1M * max_on_minutes;
heater(
ha_on := NVL_Receiver.boiler_cmd.ha_on,
ha_off := NVL_Receiver.boiler_cmd.ha_off,
schedule_on := NVL_Receiver.boiler_cmd.schedule_on,
schedule_off := NVL_Receiver.boiler_cmd.schedule_off,
emergency_stop := NVL_Receiver.boiler_cmd.emergency_stop,
max_on_time := max_on_time
);
NVL_Sender.boiler_status.state := heater.state;
NVL_Sender.boiler_status.relay_output := heater.relay_control;
NVL_Sender.boiler_status.on_time_minutes := DINT_TO_INT(heater.on_time_seconds / 60);
NVL_Sender.boiler_status.remaining_minutes := DINT_TO_INT(heater.remaining_seconds / 60);
NVL_Sender.boiler_status.emergency_active := heater.emergency_active;
NVL_Sender.boiler_status.time_limit_reached := heater.time_limit_reached;
NVL_Sender.boiler_status.error_state := heater.error_state;
NVL_Sender.boiler_status.error_code := heater.error_code;

View File

@@ -0,0 +1,22 @@
(* === DECLARATION === *)
PROGRAM Lights
VAR
masterBedroom : fb_switch;
masterBathroom: fb_switch;
bedroom_1: fb_switch;
bedroom_2: fb_switch;
bathroom: fb_switch;
guest_wc: fb_switch;
kitchen: fb_switch;
pantry: fb_switch;
livingRoom: fb_switch;
diningRoom: fb_switch;
veranda: fb_switch;
entrance: fb_switch;
front: fb_switch;
back: fb_switch;
side: fb_switch;
hallway: fb_switch;
END_VAR
(* === IMPLEMENTATION === *)

View File

@@ -0,0 +1,116 @@
(* === DECLARATION === *)
(* Room lighting (redesign). Shower channels l_1..l_5 only l_6 is water heater (Boiler_PRG). *)
PROGRAM Lights_new
VAR
Living_Room: FB_ROOM;
Dininng_Room: fb_room;
Kitchen_Area: fb_room;
Hallway: fb_room;
Office: fb_room;
Bedroom: fb_room;
Master_Bedroom: fb_room;
Master_bath: fb_room;
Shower: fb_room;
Guest_wc: fb_room;
Outside_front: fb_room;
Covered_Veranda: fb_room;
Yard: fb_room;
Outside_back: fb_room;
END_VAR
(* === IMPLEMENTATION === *)
Living_Room(
switches := NVL_Receiver.cmd_livingRoom,
relay_status := NVL_Sender.light_livingRoom
);
NVL_Sender.light_livingRoom := Living_Room.lights;
Dininng_Room(
switches := NVL_Receiver.cmd_dinningRoom,
relay_status := NVL_Sender.light_dinningRoom
);
NVL_Sender.light_dinningRoom := Dininng_Room.lights;
Kitchen_Area(
switches := NVL_Receiver.cmd_kitchenArea,
relay_status := NVL_Sender.light_kitchenArea
);
NVL_Sender.light_kitchenArea := Kitchen_Area.lights;
Hallway(
switches := NVL_Receiver.cmd_hallway,
relay_status := NVL_Sender.light_hallway
);
NVL_Sender.light_hallway := Hallway.lights;
Office(
switches := NVL_Receiver.cmd_office,
relay_status := NVL_Sender.light_office
);
NVL_Sender.light_office := Office.lights;
Bedroom(
switches := NVL_Receiver.cmd_bedroom,
relay_status := NVL_Sender.light_bedroom
);
NVL_Sender.light_bedroom := Bedroom.lights;
Master_Bedroom(
switches := NVL_Receiver.cmd_masterBedroom,
relay_status := NVL_Sender.light_masterBedroom
);
NVL_Sender.light_masterBedroom := Master_Bedroom.lights;
Master_bath(
switches := NVL_Receiver.cmd_masterBath,
relay_status := NVL_Sender.light_masterBath
);
NVL_Sender.light_masterBath := Master_bath.lights;
Shower(
switches := NVL_Receiver.cmd_shower,
relay_status := NVL_Sender.light_shower
);
// l_6 not used for shower water heater uses boiler_status.relay_output (%QX5.4)
NVL_Sender.light_shower.l_6 := FALSE;
NVL_Sender.light_shower.l_6_status := FALSE;
NVL_Sender.light_shower.l_1 := Shower.lights.l_1;
NVL_Sender.light_shower.l_2 := Shower.lights.l_2;
NVL_Sender.light_shower.l_3 := Shower.lights.l_3;
NVL_Sender.light_shower.l_4 := Shower.lights.l_4;
NVL_Sender.light_shower.l_5 := Shower.lights.l_5;
NVL_Sender.light_shower.l_1_status := Shower.lights.l_1;
NVL_Sender.light_shower.l_2_status := Shower.lights.l_2;
NVL_Sender.light_shower.l_3_status := Shower.lights.l_3;
NVL_Sender.light_shower.l_4_status := Shower.lights.l_4;
NVL_Sender.light_shower.l_5_status := Shower.lights.l_5;
Guest_wc(
switches := NVL_Receiver.cmd_guestWc,
relay_status := NVL_Sender.light_guestWc
);
NVL_Sender.light_guestWc := Guest_wc.lights;
Outside_front(
switches := NVL_Receiver.cmd_outFront,
relay_status := NVL_Sender.light_outFront
);
NVL_Sender.light_outFront := Outside_front.lights;
Covered_Veranda(
switches := NVL_Receiver.cmd_coveredVeranda,
relay_status := NVL_Sender.light_coveredVeranda
);
NVL_Sender.light_coveredVeranda := Covered_Veranda.lights;
Yard(
switches := NVL_Receiver.cmd_outYard,
relay_status := NVL_Sender.light_outYard
);
NVL_Sender.light_outYard := Yard.lights;
Outside_back(
switches := NVL_Receiver.cmd_outBack,
relay_status := NVL_Sender.light_outBack
);
NVL_Sender.light_outBack := Outside_back.lights;