Remove obsolete INFORMATION_NEEDED.md and streamline README.md for clarity

- Deleted the INFORMATION_NEEDED.md file as it was no longer necessary for documentation.
- Revised README.md to enhance the overview of the home automation system, focusing on how services and CODESYS connectivity work.
- Updated project structure in README.md for better organization and clarity, including links to relevant documentation.

This update simplifies the documentation and improves the overall user experience for developers and users of the home automation system.
This commit is contained in:
2026-02-08 17:09:30 +02:00
parent 6ef31cd12a
commit f0d883b8c2
24 changed files with 15006 additions and 3673 deletions

View File

@@ -1,127 +0,0 @@
# Information Needed for Documentation
This document outlines the information needed to complete the home automation system documentation. Please provide as much detail as possible for each section.
## 1. Network Configuration
- [ ] IP addresses for all devices:
- Raspberry Pi (CODESYS)
- Node-RED host
- MQTT broker (LXC container)
- Zigbee2MQTT host
- Home Assistant host
- [ ] Network topology (VLANs, subnets, etc.)
- [ ] Port numbers used by services (MQTT, Node-RED, Home Assistant, etc.)
- [ ] Firewall rules or network restrictions
## 2. CODESYS Configuration
- [ ] CODESYS version and license type
- [ ] Network variable names and types (sent to Node-RED)
- [ ] Network variable names and types (received from Node-RED)
- [ ] EtherCAT IO card model and configuration
- [ ] Relay mapping (which relay controls which light/device)
- [ ] Water boiler control logic
- [ ] Project file location or export (if available)
## 3. Node-RED Configuration
- [ ] Node-RED version
- [ ] Flow export or description of main flows
- [ ] MQTT topics used for communication
- [ ] CODESYS network variable integration details
- [ ] Custom nodes or functions used
- [ ] Automation logic descriptions
## 4. MQTT Broker
- [ ] MQTT broker software (Mosquitto, Eclipse, etc.) and version
- [ ] LXC container specifications
- [ ] Authentication configuration (users, passwords, ACLs)
- [ ] Topic structure and naming conventions
- [ ] Retain policies and QoS levels
## 5. Zigbee2MQTT
- [ ] Zigbee2MQTT version
- [ ] Coordinator device model
- [ ] List of Zigbee devices (switches, sensors, etc.) with:
- Device names
- Model numbers
- MQTT topics
- Functions/features
- [ ] Configuration file location or export
- [ ] Network key (if you're comfortable sharing, or note that it's stored securely)
## 6. Home Assistant
- [ ] Home Assistant version and installation type (HassOS, Docker, etc.)
- [ ] Integration configurations:
- MQTT integration settings
- Node-RED integration (if used)
- Zigbee integration (if direct)
- [ ] Dashboard screenshots or descriptions
- [ ] Automation rules
- [ ] Custom components or add-ons
## 7. Hardware Inventory
- [ ] Raspberry Pi model and specifications
- [ ] Beckhoff EtherCAT IO card model number
- [ ] Relay module details (model, number of channels)
- [ ] Zigbee coordinator hardware
- [ ] Zigbee switch models and quantities
- [ ] Water boiler model and control interface
- [ ] Any other connected hardware
## 8. Physical Wiring
- [ ] Relay to device mapping (which relay controls which physical device)
- [ ] Wiring diagrams or descriptions
- [ ] Safety considerations or warnings
## 9. Automation Logic
- [ ] How light switches trigger actions
- [ ] Water boiler control logic and schedules
- [ ] Any time-based automations
- [ ] Error handling and fallback behaviors
- [ ] Manual override procedures
## 10. Access Information (Optional)
If you'd like me to access services directly to gather information:
- [ ] SSH access details (if comfortable)
- [ ] Web interface URLs and credentials (if comfortable)
- [ ] API endpoints and authentication
- [ ] File system paths to configuration files
**Note:** Only provide access information if you're comfortable doing so. I can work with exported configuration files instead.
## 11. Troubleshooting & Known Issues
- [ ] Common problems and solutions
- [ ] Known limitations
- [ ] Planned improvements
## Priority Items
To get started quickly, the most important information is:
1. Network IP addresses and topology
2. CODESYS network variable names
3. MQTT topic structure
4. Zigbee device list
5. Relay to device mapping
---
**How to Provide Information:**
1. Fill out this checklist and provide details in a separate document
2. Export configuration files and share them
3. Grant access to services (if comfortable)
4. Provide screenshots of configurations
5. Schedule a walkthrough session
Let me know your preferred method!

View File

@@ -1,56 +1,35 @@
# Home Automation System # Home Automation
## Overview How home automation works in this house: **services** and **CODESYS** connectivity.
This project documents a comprehensive home automation system built around a Raspberry Pi running CODESYS, integrated with Node-RED, Zigbee2MQTT, MQTT broker, and Home Assistant. ## How it works
## System Architecture - **CODESYS** (Raspberry Pi) runs the PLC: lights and water boiler via EtherCAT I/O.
- **Node-RED** runs automation logic and talks to CODESYS over **UDP (network variables)**.
- **MQTT** broker (LXC) is the message bus.
- **Zigbee2MQTT** bridges Zigbee switches to MQTT.
- **Home Assistant** is the dashboard and can trigger automations.
- **CODESYS Runtime** (Raspberry Pi) — Industrial control, physical I/O Flow: Zigbee switch or HA → Node-RED → UDP → CODESYS → EtherCAT → relays/lights or boiler.
- **Beckhoff EtherCAT IO** — Relays for lighting and water boiler
- **Node-RED** — Automation logic and integration
- **MQTT Broker** (LXC) — Message broker
- **Zigbee2MQTT** — Zigbee ↔ MQTT bridge
- **Home Assistant** — Dashboard and automation
- **Zigbee Switches** — Wireless light switches
## Project Structure ## Project layout
``` ```
kkelomatic_home/ kkelomatic_home/
├── codesys/ # CODESYS project and exports ├── codesys/ # CODESYS project and source (PLC logic, NVL)
│ ├── Home_Automation (1).project ├── docs/ # Documentation (architecture, services, Codesys)
│ ├── Home_Automation.export └── scripts/ # parse-codesys-xml.py
│ ├── Home_Automation.xml
│ └── Home_Automation.projectarchive
├── docs/ # Documentation
│ ├── README.md # Documentation index (start here)
│ ├── codesys/ # CODESYS docs
│ ├── integration/ # Node-RED, MQTT, HA, Zigbee
│ ├── redesign/ # Redesign and implementation
│ └── reference/ # Architecture, hardware, setup
├── scripts/ # Helper scripts
│ └── parse-codesys-xml.py
├── INFORMATION_NEEDED.md
└── README.md
``` ```
## Documentation ## Documentation
See **[docs/README.md](docs/README.md)** for the full documentation index. - **Architecture & setup:** [docs/reference/architecture.md](docs/reference/architecture.md), [setup.md](docs/reference/setup.md)
- **Services:** [docs/integration/](docs/integration/) — MQTT, Node-RED, Home Assistant, Zigbee2MQTT
- **CODESYS:** [docs/codesys/codesys.md](docs/codesys/codesys.md), [codesys/src/NVL/README.md](codesys/src/NVL/README.md) (connectivity)
### Quick links ## CODESYS connectivity
- [Architecture](docs/reference/architecture.md)
- [CODESYS Configuration](docs/codesys/codesys.md)
- [Setup Guide](docs/reference/setup.md)
- [Redesign Summary](docs/redesign/redesign-summary.md)
- [Light Naming Configuration](docs/redesign/light-naming-configuration.md)
## Information Needed - **NVL_Out** (PLC → Node-RED): light states + boiler status, UDP.
- **NVL_In** (Node-RED → PLC): light and boiler commands, UDP.
See [INFORMATION_NEEDED.md](INFORMATION_NEEDED.md) for whats required to complete the documentation. See [codesys/src/NVL/README.md](codesys/src/NVL/README.md) and [nodered-payload.md](codesys/src/NVL/nodered-payload.md) for layout and ports.
## Maintenance
- Last Updated: January 27, 2026
- System Status: Operational

View File

@@ -48,7 +48,7 @@ This folder describes the **network variable** setup used for CODESYS ↔ Node-R
**In CODESYS:** Create an **NVL Receiver**, bind it to the **NVL_In** structure. Set protocol to UDP, listen port, task and interval. The PLC will overwrite `NVL_In` with received data each cycle. **In CODESYS:** Create an **NVL Receiver**, bind it to the **NVL_In** structure. Set protocol to UDP, listen port, task and interval. The PLC will overwrite `NVL_In` with received data each cycle.
**In Node-RED:** Send one UDP packet per update (or cyclic) with the same binary layout as `NVL_In` so CODESYS can unpack it into the receiver struct. **In Node-RED:** Use a **UDP out** node to the PLC IP and NVL Receiver port. Send one UDP packet per update with the same binary layout as `NVL_In`; see [nodered-payload.md](nodered-payload.md) for building the buffer.
--- ---
@@ -76,7 +76,5 @@ This folder describes the **network variable** setup used for CODESYS ↔ Node-R
## 4. Node-RED usage ## 4. Node-RED usage
- **Receive (PLC → Node-RED):** Use a **UDP in** node listening on the port configured in the CODESYS NVL Sender (e.g. 5555). The payload is binary; see [nodered-payload.md](nodered-payload.md) for struct layout and how to parse it to JSON for MQTT/HA. - **Receive (PLC → Node-RED):** **UDP in** node on the NVL Sender port (e.g. 5555). Payload is binary; see [nodered-payload.md](nodered-payload.md) for layout and parsing.
- **Send (Node-RED → PLC):** Use a **UDP out** node targeting the PLC IP and the NVL Receiver port (e.g. 5556). Build the payload buffer from your command (e.g. from MQTT/HA) using the same layout as [nodered-payload.md](nodered-payload.md). - **Send (Node-RED → PLC):** **UDP out** node to PLC IP and NVL Receiver port (e.g. 5556). Build the buffer from [nodered-payload.md](nodered-payload.md).
See **nodered-payload.md** in this folder for byte layout and example parsing/building.

View File

@@ -39,12 +39,76 @@ masterBathroom(switches := NVL_In.masterBathroom, relay_status := EtherCAT_Relay
EtherCAT_RelayFeedback.masterBathroom := masterBathroom.lights; EtherCAT_RelayFeedback.masterBathroom := masterBathroom.lights;
NVL_Out.l_masterBathroom := masterBathroom.lights; NVL_Out.l_masterBathroom := masterBathroom.lights;
// Bedroom 1..2, bathroom, guest_wc, kitchen, pantry, livingRoom, diningRoom, // Bedroom 1
// entrance, hallway, veranda, front, back, side: same pattern (fb_room, RelayFeedback, NVL_Out).
bedroom_1(switches := NVL_In.bedroom_1, relay_status := EtherCAT_RelayFeedback.bedroom_1); bedroom_1(switches := NVL_In.bedroom_1, relay_status := EtherCAT_RelayFeedback.bedroom_1);
EtherCAT_RelayFeedback.bedroom_1 := bedroom_1.lights; EtherCAT_RelayFeedback.bedroom_1 := bedroom_1.lights;
NVL_Out.l_bedroom_1 := bedroom_1.lights; NVL_Out.l_bedroom_1 := bedroom_1.lights;
// Bedroom 2
bedroom_2(switches := NVL_In.bedroom_2, relay_status := EtherCAT_RelayFeedback.bedroom_2);
EtherCAT_RelayFeedback.bedroom_2 := bedroom_2.lights;
NVL_Out.l_bedroom_2 := bedroom_2.lights;
// Bathroom
bathroom(switches := NVL_In.bathroom, relay_status := EtherCAT_RelayFeedback.bathroom);
EtherCAT_RelayFeedback.bathroom := bathroom.lights;
NVL_Out.l_bathroom := bathroom.lights;
// Guest WC
guest_wc(switches := NVL_In.guestWc, relay_status := EtherCAT_RelayFeedback.guest_wc);
EtherCAT_RelayFeedback.guest_wc := guest_wc.lights;
NVL_Out.l_guestWc := guest_wc.lights;
// Kitchen
kitchen(switches := NVL_In.kitchen, relay_status := EtherCAT_RelayFeedback.kitchen);
EtherCAT_RelayFeedback.kitchen := kitchen.lights;
NVL_Out.l_kitchen := kitchen.lights;
// Pantry
pantry(switches := NVL_In.pantry, relay_status := EtherCAT_RelayFeedback.pantry);
EtherCAT_RelayFeedback.pantry := pantry.lights;
NVL_Out.l_pantry := pantry.lights;
// Living Room
livingRoom(switches := NVL_In.livingRoom, relay_status := EtherCAT_RelayFeedback.livingRoom);
EtherCAT_RelayFeedback.livingRoom := livingRoom.lights;
NVL_Out.l_livingRoom := livingRoom.lights;
// Dining Room
diningRoom(switches := NVL_In.dinningRoom, relay_status := EtherCAT_RelayFeedback.diningRoom);
EtherCAT_RelayFeedback.diningRoom := diningRoom.lights;
NVL_Out.l_dinningRoom := diningRoom.lights;
// Entrance
entrance(switches := NVL_In.entrance, relay_status := EtherCAT_RelayFeedback.entrance);
EtherCAT_RelayFeedback.entrance := entrance.lights;
NVL_Out.l_entrance := entrance.lights;
// Hallway
hallway(switches := NVL_In.hallway, relay_status := EtherCAT_RelayFeedback.hallway);
EtherCAT_RelayFeedback.hallway := hallway.lights;
NVL_Out.l_hallway := hallway.lights;
// Veranda
veranda(switches := NVL_In.outVeranda, relay_status := EtherCAT_RelayFeedback.veranda);
EtherCAT_RelayFeedback.veranda := veranda.lights;
NVL_Out.l_outVeranda := veranda.lights;
// Front
front(switches := NVL_In.outFront, relay_status := EtherCAT_RelayFeedback.front);
EtherCAT_RelayFeedback.front := front.lights;
NVL_Out.l_outFront := front.lights;
// Back
back(switches := NVL_In.outBack, relay_status := EtherCAT_RelayFeedback.back);
EtherCAT_RelayFeedback.back := back.lights;
NVL_Out.l_outBack := back.lights;
// Side
side(switches := NVL_In.outSide, relay_status := EtherCAT_RelayFeedback.side);
EtherCAT_RelayFeedback.side := side.lights;
NVL_Out.l_outSide := side.lights;
// ========== SECTION 2: BOILER ========== // ========== SECTION 2: BOILER ==========
boiler( boiler(

View File

@@ -1,30 +1,24 @@
# Documentation Index # Documentation
## Reference (Architecture & Setup) Basics: **how home automation works**, **services**, and **CODESYS connectivity**.
- [Architecture Overview](reference/architecture.md)
- [Hardware Inventory](reference/hardware.md) ## Reference (architecture & setup)
- [Network Topology](reference/network.md)
- [Setup Guide](reference/setup.md) - [Architecture](reference/architecture.md) — Components and data flow
- [Quick Reference](reference/quick-reference.md) - [Hardware](reference/hardware.md) — Raspberry Pi, EtherCAT, Zigbee
- [Network](reference/network.md) — Topology
- [Setup](reference/setup.md) — Installation order
- [Quick reference](reference/quick-reference.md)
## Services
- [MQTT](integration/mqtt.md) — Broker (LXC)
- [Node-RED](integration/nodered.md) — Automation and CODESYS bridge
- [Home Assistant](integration/homeassistant.md) — Dashboard
- [Zigbee2MQTT](integration/zigbee2mqtt.md) — Zigbee ↔ MQTT
## CODESYS ## CODESYS
- [CODESYS Configuration](codesys/codesys.md)
- [CODESYS Export Guide](codesys/codesys-export-guide.md)
- [CODESYS Project Analysis](codesys/codesys-analysis.md)
- [CODESYS XML Analysis](codesys/codesys-xml-analysis.md)
## Integration (Node-RED, MQTT, HA, Zigbee) - [CODESYS configuration](codesys/codesys.md) — Runtime, EtherCAT, NVL
- [Node-RED](integration/nodered.md) - [PLC algorithm design](codesys/plc-algorithm-design.md) — Logic and I/O
- [MQTT Broker](integration/mqtt.md) - **NVL (connectivity):** [codesys/src/NVL/README.md](../codesys/src/NVL/README.md) — UDP, NVL_In / NVL_Out, payload layout
- [Home Assistant](integration/homeassistant.md)
- [Zigbee2MQTT](integration/zigbee2mqtt.md)
## Redesign (Structure & Implementation)
- [Redesign Summary](redesign/redesign-summary.md) — **Start here for redesign**
- [Light Naming Configuration](redesign/light-naming-configuration.md) — Configure lights per room
- [Implementation Roadmap](redesign/redesign-implementation-roadmap.md)
- [Structure Redesign Analysis](redesign/structure-redesign-analysis.md)
- [Structure Comparison (Visual)](redesign/structure-comparison-visual.md)
- [fb_switch Documentation](redesign/fb_switch-documentation.md)
- [fb_switch Redesign Recommendation](redesign/fb_switch-redesign-recommendation.md)
- [fb_switch Analysis Summary](redesign/fb_switch-analysis-summary.md)

View File

@@ -1,189 +0,0 @@
# CODESYS Project Analysis
## Overview
This document contains information extracted from the CODESYS project export file (`Home_Automation.export`).
## Device Information
- **Device Name**: `codesys-home`
- **CODESYS Version**: 4.15.0.0
- **Platform**: CODESYS Control for Raspberry Pi MC SL
- **SDK Version**: 3.5.21.0
- **Order Number**: 2302000009
## Hardware Configuration
### EtherCAT Network
- **EtherCAT Master**: Version 4.9.0.0
- **EtherCAT Task**: `EtherCAT_Task`
### EtherCAT Devices
1. **EK1100** - EtherCAT Coupler (2A E-Bus)
- Vendor: Beckhoff Automation GmbH & Co. KG
- Order Number: EK1100
- Function: EtherCAT coupler connecting to E-Bus modules
2. **EL1809** - 16Ch. Digital Input 24V, 3ms
- Vendor: Beckhoff Automation GmbH & Co. KG
- Order Number: EL1809
- Function: 16-channel digital input module
- Channels: 16 digital inputs (Channels 1-16)
3. **Output Module** (Likely EL2004 or similar)
- 16 output channels configured
- Channel addresses: 16#1A00 through 16#1A0F (Channels 1-16)
## Network Variables
CODESYS communicates with Node-RED using UDP-based network variables.
### Variables Sent to Node-RED (NVL_Sender)
**List Identifier**: 1
**Protocol**: UDP
**Task**: EtherCAT_Task
**Transmission**: Cyclic
**Interval**: T#50ms
**Minimum Gap**: T#20ms
**Structure**: `struct_lights` containing light states for each room:
| Variable Name | Type | Description |
|--------------|------|-------------|
| `l_masterBedroom` | struct_lights | Master bedroom lights |
| `l_masterBathroom` | struct_lights | Master bathroom lights |
| `l_bedroom_1` | struct_lights | Bedroom 1 lights |
| `l_bedroom_2` | struct_lights | Bedroom 2 lights |
| `l_bathroom` | struct_lights | Bathroom lights |
| `l_hallway` | struct_lights | Hallway lights |
| `l_pantry` | struct_lights | Pantry lights |
| `l_kitchen` | struct_lights | Kitchen lights |
| `l_livingRoom` | struct_lights | Living room lights |
| `l_dinningRoom` | struct_lights | Dining room lights |
| `l_entrance` | struct_lights | Entrance lights |
| `l_guestWc` | struct_lights | Guest WC lights |
| `l_outVeranda` | struct_lights | Outdoor veranda lights |
| `l_outFront` | struct_lights | Outdoor front lights |
| `l_outSide` | struct_lights | Outdoor side lights |
| `l_outBack` | struct_lights | Outdoor back lights |
### Variables Received from Node-RED (NVL_Receiver)
**List Identifier**: 3
**Protocol**: UDP
**Task**: EtherCAT_Task
**Transmission**: Cyclic
**Interval**: T#50ms
**Minimum Gap**: T#20ms
**Import File**: `C:\Users\NearchosParaskeva\OneDrive - individual\Codesys_Home_Automation\NVL_Receiver.gvl`
**Structure**: `struct_switches` containing switch states for each room:
| Variable Name | Type | Description |
|--------------|------|-------------|
| `masterBedroom` | struct_switches | Master bedroom switches |
| `masterBathroom` | struct_switches | Master bathroom switches |
| `bedroom_1` | struct_switches | Bedroom 1 switches |
| `bedroom_2` | struct_switches | Bedroom 2 switches |
| `bathroom` | struct_switches | Bathroom switches |
| `hallway` | struct_switches | Hallway switches |
| `pantry` | struct_switches | Pantry switches |
| `kitchen` | struct_switches | Kitchen switches |
| `livingRoom` | struct_switches | Living room switches |
| `dinningRoom` | struct_switches | Dining room switches |
| `entrance` | struct_switches | Entrance switches |
| `guestWc` | struct_switches | Guest WC switches |
| `outVeranda` | struct_switches | Outdoor veranda switches |
| `outFront` | struct_switches | Outdoor front switches |
| `outSide` | struct_switches | Outdoor side switches |
| `outBack` | struct_switches | Outdoor back switches |
## Program Structure
### Main Program (PLC_PRG)
- **Type**: Program
- **Language**: Structured Text (ST)
- **Function Blocks Used**: `fb_switch`
- **Instances**:
- `masterBedroom: fb_switch`
### Function Blocks
- **fb_toogleButton**: Toggle button function block
- **fb_switch**: Switch control function block
### Variables Used in Logic
From the program code, the following variables are referenced:
- `lights.l_1`, `lights.l_2`, `lights.l_3`, `lights.l_4`, `lights.l_5`, `lights.l_6`
- `switches.sw_1`, `switches.sw_2`, `switches.sw_3`, `switches.sw_4`, `switches.sw_5`, `switches.sw_6`
- `switches.all_off`
- `switches.all_on`
## Room Configuration
The system controls lighting for 15 rooms/areas:
1. Master Bedroom
2. Master Bathroom
3. Bedroom 1
4. Bedroom 2
5. Bathroom
6. Hallway
7. Pantry
8. Kitchen
9. Living Room
10. Dining Room
11. Entrance
12. Guest WC
13. Outdoor Veranda
14. Outdoor Front
15. Outdoor Side
16. Outdoor Back
## I/O Mapping
### Inputs (EL1809)
- 16 digital input channels
- Likely used for local switch inputs or status feedback
### Outputs (16 channels, likely EL2004)
- 16 digital output channels (16#1A00 - 16#1A0F)
- Used to control relay outputs for lighting
**Note**: The exact mapping between relay outputs and rooms needs to be determined from the program logic or wiring documentation.
## Libraries Used
- **IoStandard** (System)
- **SM3_Basic** (3S - Smart Software Solutions GmbH)
- **SM3_CNC** (3S - Smart Software Solutions GmbH)
- **3SLicense** (CODESYS)
- **SM3_CamBuilder** (CODESYS)
- **SM3_Robotics** (CODESYS)
- **SM3_Robotics_Visu** (CODESYS)
- **SM3_Transformation** (CODESYS)
- **Standard** (System)
- **CmpLog** (System)
## Task Configuration
- **EtherCAT Task**: Used for EtherCAT communication and network variable updates
- **Update Rate**: 50ms (cyclic transmission)
## Notes
- The project uses structured data types (`struct_lights`, `struct_switches`) for organizing room data
- Network variables are packed and transmitted via UDP
- The system appears to support both indoor and outdoor lighting control
- Water boiler control mentioned in documentation was not found in the export - may be in a separate module or not yet implemented
---
**Analysis Date**: January 27, 2026
**Source File**: `Home_Automation.export`
**Export Format**: CODESYS XML

View File

@@ -1,147 +0,0 @@
# CODESYS Project Export Guide
## Overview
CODESYS projects can be exported to readable XML formats that allow for better analysis, documentation, and version control. The binary `.project` file format is proprietary and not human-readable, but CODESYS provides export options that create XML files.
## Export Formats
### 1. CODESYS XML Format (*.export)
**Best for:** Full project export, version control, complete documentation
- **Format:** Fully compatible CODESYS XML format
- **Content:** Complete project structure in machine-parsable XML
- **Compatibility:** 100% compatible with CODESYS
- **Use Cases:**
- Full project backup
- Version control systems
- Automated analysis and documentation
- Project migration
**How to Export:**
1. Open your project in CODESYS IDE
2. Go to **Project → Export**
3. Select the objects you want to export (or select all)
4. Choose **CODESYS XML Format (*.export)** as the file type
5. Save the file (e.g., `Home_Automation.export`)
### 2. PLCopen XML Format (*.xml)
**Best for:** Interoperability, documentation tools, program structure
- **Format:** PLCopen XML standard
- **Content:** Subset of CODESYS elements (programs, function blocks, variables)
- **Compatibility:** May not include all CODESYS-specific features
- **Use Cases:**
- Documentation generation
- Integration with other tools
- Program structure analysis
- Cross-platform compatibility
**How to Export:**
1. Open your project in CODESYS IDE
2. Go to **Project → Export PLCopenXML**
3. Select the objects to export
4. Save the file (e.g., `Home_Automation.xml`)
## What Information Can Be Extracted
From exported XML files, we can extract:
### Project Structure
- Program Organization Units (POUs)
- Function blocks and functions
- Libraries and dependencies
- Project hierarchy
### Network Variables
- Variable names
- Data types
- Initial values
- Network variable declarations
- Direction (input/output)
### I/O Configuration
- EtherCAT device configuration
- I/O channel mappings
- Device parameters
- Scan rates
### Control Logic
- Program code structure
- Variable declarations
- Function block instances
- Logic flow
### Hardware Configuration
- Device configurations
- Network settings
- Communication parameters
## Recommended Export Process
For comprehensive documentation, export both formats:
1. **Full Export (CODESYS XML):**
```
File: Home_Automation.export
- Complete project structure
- All configurations
- Network variables
- I/O mappings
```
2. **Program Export (PLCopen XML):**
```
File: Home_Automation.xml
- Program structure
- Variable declarations
- Logic organization
```
## After Export
Once you have the exported XML files:
1. **Share the files** - The XML files can be analyzed and documented
2. **Version control** - XML files are text-based and work well with Git
3. **Documentation** - Information can be extracted and added to the docs
4. **Analysis** - Scripts can parse the XML to extract specific information
## Next Steps
After exporting:
1. Place exported files in the project directory
2. Update `.gitignore` if needed (or commit the exports)
3. Use the exported files to:
- Document network variables
- Map I/O configurations
- Understand control logic
- Complete the documentation templates
## Example Export Workflow
```bash
# In CODESYS IDE:
1. File → Open → "Home_Automation (1).project"
2. Project → Export
- Select: All objects
- Format: CODESYS XML (*.export)
- Save as: "Home_Automation.export"
3. Project → Export PLCopenXML
- Select: Programs, POUs
- Save as: "Home_Automation.xml"
```
## Notes
- Export files are typically much larger than binary project files
- XML exports are human-readable and can be opened in any text editor
- Some CODESYS-specific features may not be fully represented in PLCopen XML
- Exported files can be re-imported into CODESYS if needed
---
**Status:** Ready for export - follow the steps above to create readable project files

View File

@@ -1,260 +0,0 @@
# CODESYS PLCopen XML Analysis
## Overview
This document contains detailed information extracted from the PLCopen XML export (`Home_Automation.xml`), which provides a more structured view of the project compared to the CODESYS export format.
## Project Information
- **Format**: PLCopen XML (TC6 0200)
- **CODESYS Version**: V3.5 SP21
- **Export Date**: 2026-01-27T14:48:52
- **Project Name**: Home_Automation.project
## Task Configuration
### MainTask
- **Type**: Cyclic
- **Interval**: t#4ms (4 milliseconds)
- **Priority**: 1
- **Watchdog**: Disabled
- **Program**: PLC_PRG
### EtherCAT_Task
- **Type**: Cyclic
- **Interval**: t#4ms (4 milliseconds)
- **Priority**: 1
- **Watchdog**: Disabled
- **Purpose**: EtherCAT communication and network variable updates
## Data Structures
### struct_switches
```iec
TYPE struct_switches :
STRUCT
all_off: BOOL; // Command to turn all lights off
all_on: BOOL; // Command to turn all lights on
sw_1: BOOL; // Switch 1 state
sw_2: BOOL; // Switch 2 state
sw_3: BOOL; // Switch 3 state
sw_4: BOOL; // Switch 4 state
sw_5: BOOL; // Switch 5 state
sw_6: BOOL; // Switch 6 state
END_STRUCT
END_TYPE
```
### struct_lights
```iec
TYPE struct_lights:
STRUCT
l_1: BOOL; // Light 1 control output
l_2: BOOL; // Light 2 control output
l_3: BOOL; // Light 3 control output
l_4: BOOL; // Light 4 control output
l_5: BOOL; // Light 5 control output
l_6: BOOL; // Light 6 control output
END_STRUCT
END_TYPE
```
## Function Block: fb_toogleButton
### Interface
**Inputs**:
- `Input: BOOL` - Input signal (switch state from network)
**Outputs**:
- `Output: BOOL` - Output signal (light control)
**Internal Variables**:
- `Switch: BOOL` - Internal switch state
- `Light: BOOL` - Internal light state
- `Flag1: BOOL` - Internal flag 1
- `Flag2: BOOL` - Internal flag 2
- `RT: R_TRIG` - Rising edge trigger
- `Pulse: TP` - Pulse timer
- `CTU_0: CTU` - Counter up 0
- `CTD_0: CTD` - Counter down 0
- `CTU_1: CTU` - Counter up 1
- `CTU_2: CTU` - Counter up 2
- `TOF_0: TOF` - Timer off delay 0
### Implementation (Ladder Diagram)
**Network 1: Input Debouncing**
- Input signal → TP (Pulse Timer) with PT = t#100ms
- TP output → TOF (Timer Off Delay) with PT = t#100ms
- TOF output → Switch (internal state)
- **Purpose**: Debounces input signal with 100ms pulse and 100ms off delay
**Network 2: Flag1 Logic**
- Switch AND (NOT Light) AND (NOT Flag2) → Flag1
- **Purpose**: Sets Flag1 when switch is on, light is off, and Flag2 is off
**Network 3: Flag2 Logic**
- Switch AND Light AND (NOT Flag1) → Flag2
- **Purpose**: Sets Flag2 when switch is on, light is on, and Flag1 is off
**Network 4: Light Toggle Logic**
- Flag1 AND (NOT Flag2) → Light (toggle)
- **Purpose**: Toggles light state based on flag conditions
**Network 5: Output Assignment**
- Light → Output
- **Purpose**: Direct output assignment
### Timing Characteristics
- **Input Debounce**: 100ms pulse timer (TP)
- **Output Delay**: 100ms off delay (TOF)
- **Total Response Time**: ~200ms (debounce + delay)
## Function Block: fb_switch
### Interface
**Inputs**:
- `switches: struct_switches` - Switch states from Node-RED
**Outputs**:
- `lights: struct_lights` - Light control outputs to Node-RED
**Internal Variables**:
- `all_off: fb_toogleButton` - Toggle button for "all off"
- `all_on: fb_toogleButton` - Toggle button for "all on"
- `sw_1: fb_toogleButton` - Toggle button for switch 1
- `sw_2: fb_toogleButton` - Toggle button for switch 2
- `sw_3: fb_toogleButton` - Toggle button for switch 3
- `sw_4: fb_toogleButton` - Toggle button for switch 4
- `sw_5: fb_toogleButton` - Toggle button for switch 5
- `sw_6: fb_toogleButton` - Toggle button for switch 6
### Implementation (Continuous Function Chart - CFC)
**Logic Flow for Each Light (l_1 through l_6)**:
1. **All Off Logic**:
```
all_off.Output AND lights.l_X → AND gate → (forces light off)
```
2. **All On Logic**:
```
all_on.Output AND (NOT lights.l_X) → AND gate → (forces light on)
```
3. **Individual Switch Logic**:
```
switches.sw_X → sw_X.Input → sw_X.Output → lights.l_X
```
4. **Combined Logic (OR)**:
```
(all_on logic) OR (sw_X.Output) OR (other sources) → lights.l_X
```
**Execution Order** (example for l_1):
1. Execution Order 1: AND gate for all_on logic
2. Execution Order 2: sw_2 toggle button
3. Execution Order 3: OR gate combining sources
4. Execution Order 4: AND gate for all_off logic
5. Execution Order 5: Output assignment to lights.l_1
6. Execution Order 30: sw_1 toggle button (individual control)
**Key Logic Patterns**:
- **All Off**: Uses AND gate with `all_off.Output` and current light state
- **All On**: Uses AND gate with `all_on.Output` and inverted light state
- **Individual Control**: Each switch has its own toggle button instance
- **Priority**: All_off can override individual controls, all_on combines with individual
## Program: Lights
The "Lights" program contains instances of `fb_switch` for all 15 rooms:
1. `masterBedroom: fb_switch`
2. `masterBathroom: fb_switch`
3. `bedroom_1: fb_switch`
4. `bedroom_2: fb_switch`
5. `bathroom: fb_switch`
6. `guest_wc: fb_switch`
7. `kitchen: fb_switch`
8. `pantry: fb_switch`
9. `livingRoom: fb_switch`
10. `diningRoom: fb_switch`
11. `veranda: fb_switch`
12. `entrance: fb_switch`
13. `front: fb_switch`
14. `back: fb_switch`
15. `side: fb_switch`
16. `hallway: fb_switch`
## Network Variables
### NVL_Sender (Sent to Node-RED)
All 15 room light structures:
- `l_masterBedroom` through `l_outBack` (struct_lights)
### NVL_Receiver (Received from Node-RED)
All 15 room switch structures:
- `masterBedroom` through `outBack` (struct_switches)
## Control Logic Summary
### Individual Light Control
1. **Switch Input** → `switches.sw_X` (from Node-RED)
2. **Toggle Processing** → `fb_toogleButton` instance
- 100ms debounce (TP)
- 100ms delay (TOF)
- Toggle logic with flags
3. **Output** → `lights.l_X` (to Node-RED)
### Global Commands
**All Off**:
- `switches.all_off` → `all_off` toggle button
- Output ANDed with current light state
- Forces all lights off
**All On**:
- `switches.all_on` → `all_on` toggle button
- Output ANDed with inverted light state
- Forces all lights on
- Combined with individual switches via OR logic
### Timing Characteristics
- **Task Cycle**: 4ms (MainTask and EtherCAT_Task)
- **Input Debounce**: 100ms
- **Output Delay**: 100ms
- **Network Variable Update**: 50ms (from .export analysis)
- **Total Response**: ~200ms (debounce + delay)
## Implementation Language
- **fb_toogleButton**: Ladder Diagram (LD)
- **fb_switch**: Continuous Function Chart (CFC)
- **Lights Program**: Continuous Function Chart (CFC)
## Key Insights
1. **Debouncing**: All switch inputs are debounced with 100ms pulse timer
2. **Toggle Logic**: Uses flag-based state machine for reliable toggling
3. **Priority System**: All_off has priority over individual controls
4. **Combined Control**: All_on works in conjunction with individual switches
5. **Fast Response**: 4ms task cycle ensures responsive control
6. **Structured Design**: Each room is independent with its own fb_switch instance
---
**Analysis Date**: January 27, 2026
**Source File**: `Home_Automation.xml`
**Format**: PLCopen XML (TC6 0200)

View File

@@ -0,0 +1,31 @@
// Writes to state.rooms.cmd_livingroom (NVL variable cmd_livingroom).
const ROOM_NAME = 'cmd_livingroom';
const entityId = (msg.topic || '').toString();
const parts = entityId.split('_');
const last = parts[parts.length - 1] || '';
const num = parseInt(last, 10);
const lightNum = (!isNaN(num) && num >= 1) ? Math.min(6, num) : 1;
// Normalize state: HA / trigger-state can send payload as string, object with .state, or data.new_state.state
let rawState = msg.payload;
if (rawState !== null && typeof rawState === 'object' && rawState.state !== undefined) rawState = rawState.state;
if (msg.data && msg.data.new_state && msg.data.new_state.state !== undefined) rawState = msg.data.new_state.state;
const isOn = (rawState === 'on' || rawState === true);
node.warn('[HA to NVL] topic=' + entityId + ' rawState=' + JSON.stringify(rawState) + ' → lightNum=' + lightNum + ' isOn=' + isOn);
if (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });
const state = flow.get('nvlInState');
if (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};
const r = state.rooms[ROOM_NAME];
r['ha_l' + lightNum + '_on'] = false;
r['ha_l' + lightNum + '_off'] = false;
if (isOn) r['ha_l' + lightNum + '_on'] = true;
else r['ha_l' + lightNum + '_off'] = true;
flow.set('nvlInState', state);
node.warn('[HA to NVL] set cmd_livingroom ha_l' + lightNum + '_' + (isOn ? 'on' : 'off') + '=true, buildAndSend');
// PLC uses R_TRIG (rising edge). If "off" still doesnt work, add a flow: after this node, 80ms delay then clear ha_l*_off and buildAndSend again (pulse).
msg.payload = { buildAndSend: true };
return msg;

View File

@@ -0,0 +1,70 @@
# Node-RED flows analysis
Flows pulled from **root@10.20.30.12** (`/root/.node-red/flows.json`).
Snapshot: Feb 8, 2026.
---
## Tabs (flow sheets)
| Tab | Purpose (from nodes) |
|----------------|-----------------------------|
| **Flow 1** | CODESYS NVL (UDP), HA buttons, Room Lights subflow, dashboard links |
| Flow 27 | (Unnamed / per-room or feature) |
| **Living Room**| Living roomspecific logic → **[Living Room flow doc](nodered-livingroom-flow.md)** |
| **Notifications** | Notifications (Telegram, etc.) |
| **Zigbee** | Zigbee2MQTT in/out, MQTT |
| **Scheduler** | cronplus, bigtimer, triggers |
---
## Node counts (summary)
| Category | Node types / count |
|-----------------|--------------------|
| **UI** | 128 ui_button, 17 ui_tab, 17 ui_group, 1 ui_base |
| **Logic** | 59 function, 12 change, 10 switch, 17 inject, 2 trigger, 2 delay, 2 bigtimer, 3 cronplus |
| **Home Assistant** | 47 ha-entity-config, 29 ha-button, 18 ha-binary-sensor, 15 ha-device-config, 5 api-current-state, 2 api-call-service, 1 server-state-changed, 1 poll-state, 1 trigger-state, 1 ha-sensor |
| **Zigbee** | 28 zigbee2mqtt-in, 9 zigbee2mqtt-out, 1 zigbee2mqtt-server, 1 zigbee2mqtt-get |
| **CODESYS / NVL** | 1 nvl-send, 1 nvl-receive, 1 nvl-datatypes, 1 udp in, 1 udp out |
| **Other** | 1 server (HA WebSocket), 1 mqtt-broker, 2 mqtt in, 9 http request, 2 telegram bot, 1 telegram sender, 1 telegram receiver, 43 debug, 29 link in, 29 link out |
| **Subflows** | Room Lights (57bd1b149526fcee), Room filter (4dc13919bcc9d676), Subflow 1 |
---
## CODESYS connectivity (Flow 1)
All NVL and UDP nodes are on tab **Flow 1** (`z": "46892ced77481340"`).
| Node | Role | Config |
|-----------|------|--------|
| **udp out** | Send to PLC | `addr`: **10.20.30.5**, `port`: **1202** |
| **udp in** | Receive from PLC | `port`: **1202** |
| **nvl-send** | Build NVL_In payload | Feeds **udp out**. Uses `node-red-contrib-nvl` with inline definition (struct_switches × 15 rooms + cmd_livingroom struct_room_cmds). |
| **nvl-receive** | Parse NVL_Out | Triggered by **udp in**. Definition: 15 × struct_lights + **light_livingRoom** as struct_room_outs. |
**Notes**
- PLC IP is **10.20.30.5**; Node-RED uses the same port **1202** for both sending to and receiving from the PLC (CODESYS NVL Sender and Receiver may use the same port or different; confirm in CODESYS).
- nvl-receive uses **struct_lights** (6 BOOLs) for 15 rooms and **struct_room_outs** (6 outputs + 6 status) only for `light_livingRoom`. Your `codesys/src/NVL` docs use struct_room_outs for all rooms; the flow is a subset.
- Room Lights subflow (**Room Lights**) is used from Flow 1 (and possibly Living Room) to map HA/buttons to room/light commands.
---
## Integrations
- **Home Assistant**: WebSocket **server** config; many **ha-entity-config** / **ha-device-config**; **ha-button**, **ha-binary-sensor**; **api-current-state**, **api-call-service**.
- **Zigbee2MQTT**: One **zigbee2mqtt-server**; **zigbee2mqtt-in** (28) and **zigbee2mqtt-out** (9) for device events and commands.
- **MQTT**: One **mqtt-broker**; **mqtt in** (2) for other topics.
- **Telegram**: **telegram bot** (2), **telegram sender**, **telegram receiver** for notifications.
- **Dashboard**: **node-red-dashboard** (ui_base, ui_tab, ui_group, ui_button) for a simple UI.
---
## File location
- **Flows (full export):** [nodered-flows.json](nodered-flows.json)
- **This analysis:** [nodered-flows-analysis.md](nodered-flows-analysis.md)
To refresh: from the project PC run
`ssh root@10.20.30.12 "cat /root/.node-red/flows.json" > docs/integration/nodered-flows.json`

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,167 @@
# Node-RED: Living Room flow
This document describes the **Living Room** tab in Node-RED: how HA and Zigbee drive NVL commands and how the 328-byte buffer is built and sent to the PLC.
---
## Flow diagram
```
[Inject] ──► [trigger-state] ◄── HA entity: input_boolean.living_room_new
├──► [HA to NVL] ──┬──► [Build NVL_In] ──┬──► [link out 26] ──► (Flow 1: to UDP → PLC)
│ │ └──► debug 39
└──► debug 36 │
[Zigbee2MQTT] ──► [Zigbee to NVL] ──┼──► [Build NVL_In]
Living Room Door Switch │ ▲
(TS0044) ├──► [80ms delay] ──► [Clear zigbee edge] ──► [Build NVL_In]
│ │
└──► debug 40 └──► debug 41
```
- **HA path:** `trigger-state` watches `input_boolean.living_room_new`**HA to NVL** updates `flow.nvlInState.rooms.cmd_livingroom` (ha_lN_on / ha_lN_off) → **Build NVL_In** → link out → Flow 1 → UDP to PLC.
- **Zigbee path:** **zigbee2mqtt-in** (Living Room Door Switch TS0044) → **Zigbee to NVL** sets zigbee_swN in state → **Build NVL_In** (send) → then **80ms delay****Clear zigbee edge** (clear zigbee_swN) → **Build NVL_In** again (second packet so PLC sees one pulse).
---
## Nodes (Living Room tab)
| Node | Type | Role |
|------|------|------|
| **trigger-state** | node-red-contrib-home-assistant-websocket | Listens to `input_boolean.living_room_new`; output 1 → HA to NVL, output 2 → blocked. |
| **Inject** | inject | Manual/startup trigger into trigger-state (optional). |
| **HA to NVL** | function | Maps HA entity state to `cmd_livingroom` in `flow.nvlInState`; sets ha_l1_on/ha_l1_off … ha_l6_on/ha_l6_off from entity ID and payload; then `buildAndSend: true`. |
| **Zigbee to NVL** | function | Maps Zigbee action (single/double/hold/release/triple/quad) to zigbee_sw1…6; updates `flow.nvlInState.rooms.livingRoom`; sets `msg.zigbeeClear` for later clear; then `buildAndSend: true`. |
| **80ms** | delay | 80 ms then passes message to Clear zigbee edge. |
| **Clear zigbee edge** | function | Clears the zigbee_swN flag in `nvlInState` and sets `buildAndSend: true` again. |
| **Build NVL_In** | function | Reads `flow.nvlInState`, builds 328-byte buffer (16 rooms × 20 bytes + boiler 8 bytes), sets `msg.payload = buf`; outputs to link out 26 and debug. |
| **link out 26** | link out | Sends built buffer to Flow 1s **to UDP** link in → **udp out** (10.20.30.5:1202). |
| **debug 3641** | debug | Optional debug (37 active). |
---
## Shared state: `flow.nvlInState`
All rooms and boiler share one object:
- **Shape:** `{ rooms: { cmd_livingroom: { ha_l1_on, ha_l1_off, … }, … }, boiler: { … } }`
- **Living Room (HA test)** writes to `state.rooms.cmd_livingroom`. **Build NVL_In** reads the full object and serializes all 16 slots + boiler into the 328-byte NVL_In layout.
Room names order in Build NVL_In (must match PLC NVL):
`masterBedroom`, `masterBathroom`, `bedroom_1`, `bedroom_2`, `bathroom`, `guestWc`, `kitchen`, `pantry`, **`cmd_livingroom`**, `dinningRoom`, `entrance`, `hallway`, `outVeranda`, `outFront`, `outBack`, `outSide`.
---
## Test variable: `cmd_livingroom`
The flow uses **`cmd_livingroom`** (the NVL variable you added for testing), not `livingRoom`. HA to NVL writes to `flow.nvlInState.rooms.cmd_livingroom`, and Build NVL_In reads that key for the living-room slot in the 328-byte buffer (same 20-byte layout as struct_room_cmds).
---
## What the trigger-state must output to turn the light ON (PLC)
The **HA to NVL** node decides ON vs OFF from the message it receives. For the PLC to turn the light **on**:
| Field | Value |
|----------|--------|
| **payload** | `'on'` (string) **or** `true` (boolean) |
| **topic** | Entity ID (e.g. `input_boolean.living_room_new`). Optional for which light 16: if the last part after `_` is a number (e.g. `living_room_2` → 2), that light is used; otherwise light **1** is used. |
So the **trigger-state** should output to the first (allowed) output:
- **Turn ON:** `msg.payload === 'on'` or `msg.payload === true` → PLC gets `ha_lN_on = true` in **cmd_livingroom**.
- **Turn OFF:** any other payload (e.g. `'off'`, `false`) → PLC gets `ha_lN_off = true` in **cmd_livingroom**.
With the current entity `input_boolean.living_room_new` there is no `_1``_6`, so it always controls **light 1** in the living room.
---
## HA to NVL (function code)
- **Room (state key):** `cmd_livingroom` (NVL variable for testing).
- **Entity ID:** from `msg.topic` (e.g. `input_boolean.living_room_new`). Light index 16 is parsed from the last `_N` in the entity ID; default 1.
- **ON/OFF:** `msg.payload === 'on'` or `true` → set `ha_lN_on = true`, else `ha_lN_off = true` (the other cleared).
- Then sets `msg.payload = { buildAndSend: true }` and returns.
### Debug logs (in Node-RED Debug sidebar)
The function uses `node.warn()` so messages appear in the **Debug** tab (with the warning icon) without needing a separate debug node:
1. **On input:** `[HA to NVL] topic=... payload=... (type ...) → lightNum=... isOn=...`
Use this to confirm what the trigger-state sent: `topic`, `payload`, its type, and the parsed `lightNum` (16) and `isOn` (true/false).
2. **After state update:** `[HA to NVL] set cmd_livingroom ha_lN_on=true, buildAndSend` (or `ha_lN_off`).
Confirms which command was written and that `buildAndSend` is being passed on.
If `isOn` is false when you expect ON, check that the trigger-state output is exactly `'on'` (string) or `true` (boolean). If `topic` is wrong or missing, the light number may default to 1.
**HA to NVL function with debug:** full code in [ha-to-nvl-function.js](ha-to-nvl-function.js) (uses **cmd_livingroom**). Copy that files contents into the Node-RED function node.
---
## Zigbee to NVL (function code)
- **Room:** `livingRoom`.
- **Action map:** `single→1, double→2, hold→3, release→4, triple→5, quad→6` → sets `zigbee_swN`.
- **Edge behaviour:** Passes `msg.zigbeeClear = { room: 'livingRoom', key: 'zigbee_swN' }` so **Clear zigbee edge** (after 80 ms) clears that flag and calls **Build NVL_In** again; PLC sees one pulse.
---
## Build NVL_In (function code)
- **Buffer:** 328 bytes (Buffer.alloc(328)).
- **Rooms:** 16 rooms × 20 bytes each: bytes 011 = ha_l1_on, ha_l1_off, … ha_l6_off; bytes 1217 = zigbee_sw1…6; bytes 1819 = ha_all_on, ha_all_off.
- **Boiler:** offset 320: ha_on, ha_off, schedule_on, schedule_off, emergency_stop, (reserved), max_on_time_minutes (INT16 LE).
- **Output:** `msg.payload = buf`; wired to **link out 26** (and debug).
---
## Zigbee device
- **Friendly name:** Living Room Door Switch (TS0044)
- **Device ID:** 0xa4c1383d7921827a
- **Server:** zigbee2mqtt-server config (id 4e20fc347c658518)
---
## Do you need Build NVL_In?
**No, if you use the nvl-send node.** The nvl-send node (node-red-contrib-nvl) does the binary packing from a payload object. So you can:
1. **Remove** the **Build NVL_In** node and the **link out 26** (and the link in “to UDP” path that receives it).
2. **Add** a single function that builds the payload object from `flow.nvlInState` and feed that into **nvl-send****udp out**.
Code for that function (copy into a function node): **[state-to-nvl-send-payload.js](state-to-nvl-send-payload.js)**. It builds the object with the 15 `struct_switches` rooms (default false) and **cmd_livingroom** from `state.rooms.cmd_livingroom`. Wire: **HA to NVL / Zigbee to NVL****state-to-nvl-send-payload****nvl-send****udp out**.
---
## Syncing PLC state back to Home Assistant (Zigbee / any source → HA UI)
When you change a light with the **Zigbee switch** (or any source that doesnt go through HA), the PLC updates but HA doesnt, so entities can stay out of sync. To keep HA in sync for **all** mapped lights:
1. **After the nvl-receive node** (Flow 1), add a **function** node and paste the code from **[nvl-to-ha-sync-livingroom.js](nvl-to-ha-sync-livingroom.js)**.
2. In that function, **`LIGHT_ENTITY_MAP`** defines which PLC room/light → HA entity to sync. Each entry is `{ room: 'payloadKey', light: 1..6, entityId: 'domain.entity_id' }`. Add one row per light (any room/zone). Example:
- `{ room: 'light_livingRoom', light: 1, entityId: 'input_boolean.living_room_new' }`
- `{ room: 'l_kitchen', light: 1, entityId: 'light.kitchen_ceiling' }`
3. Give the function **2 outputs**. Then either:
**Option A Action node:** In the palette look under **Home Assistant** for **Action** ([docs](https://zachowj.github.io/node-red-contrib-home-assistant-websocket/node/action.html)). The sync already sends `payload.action` (e.g. `input_boolean.turn_on`) and `payload.target.entity_id` (array). Output 1 → **Action** (turn_on), Output 2 → **Action** (turn_off). You can leave Action config empty and use “Block Input Overrides” off so `msg.payload` sets the action and target.
**Option B HTTP:** Use **[nvl-to-ha-http-call.js](nvl-to-ha-http-call.js)** + **http request** node; set `flow.set('ha_base_url', 'http://HA_IP:8123')` and `flow.set('ha_token', 'YOUR_TOKEN')`.
The function only sends a message when a mapped lights state **changed**, so multiple entities can be updated in one NVL cycle. When you use the Zigbee switch (or the PLC changes state any other way), the matching HA entities are updated.
---
## Connection to Flow 1 (CODESYS)
- **Option A (with Build NVL_In):** Living Room builds the buffer in a function and sends it via **link out 26** → Flow 1 **link in "to UDP"****udp out**.
- **Option B (with nvl-send only):** HA to NVL / Zigbee to NVL → **state-to-nvl-send-payload****nvl-send****udp out**. No Build NVL_In, no link nodes needed for this path.
- The PLC (CODESYS) receives NVL_In on port 1202 and drives lights/boiler from the packed struct.
---
## File reference
- **Flows export:** [nodered-flows.json](nodered-flows.json) — Living Room tab id: `7de41d810b04d992`
- **Payload layout (PLC/Node-RED):** [codesys/src/NVL/nodered-payload.md](../../codesys/src/NVL/nodered-payload.md)

View File

@@ -0,0 +1,30 @@
// Use when you don't use the Action node: prepares one msg for the "http request" node to call HA.
// Accepts either: (1) Action format: payload.action + payload.target.entity_id, or (2) payload.entity_id + msg.service.
// Set ha_base_url and ha_token in flow or env.
const HA_BASE_URL = flow.get('ha_base_url') || 'http://homeassistant.local:8123';
const HA_TOKEN = flow.get('ha_token') || env.get('HA_TOKEN') || '';
const p = msg.payload || {};
let entityId = p.target && p.target.entity_id && p.target.entity_id[0];
let domain = 'input_boolean';
let service = 'turn_off';
if (p.action) {
const parts = p.action.split('.');
domain = parts[0];
service = parts[1] || 'turn_off';
}
if (!entityId) entityId = p.entity_id;
if (!entityId) return null;
if (!p.action) domain = (entityId + '').split('.')[0];
const url = HA_BASE_URL.replace(/\/$/, '') + '/api/services/' + domain + '/' + service;
msg.url = url;
msg.method = 'POST';
msg.headers = {
'Authorization': 'Bearer ' + HA_TOKEN,
'Content-Type': 'application/json'
};
msg.payload = { entity_id: entityId };
return msg;

View File

@@ -0,0 +1,33 @@
// Run this AFTER nvl-receive. Syncs all mapped lights from PLC state to HA entities.
// Connect output 1 → Action node (turn_on), output 2 → Action node (turn_off).
// Format matches node-red-contrib-home-assistant-websocket "Action" node: payload.action + payload.target.entity_id (array).
// Only outputs when state changed (per entity). Add more entries to LIGHT_ENTITY_MAP as you add rooms/lights.
// Map: NVL room key (in payload) + light index 1..6 → HA entity_id
const LIGHT_ENTITY_MAP = [
{ room: 'light_livingRoom', light: 1, entityId: 'input_boolean.living_room_new' },
// { room: 'light_livingRoom', light: 2, entityId: 'input_boolean.living_room_2' },
// { room: 'l_kitchen', light: 1, entityId: 'light.kitchen_ceiling' },
];
const payload = msg.payload || {};
const onMsgs = [];
const offMsgs = [];
for (const entry of LIGHT_ENTITY_MAP) {
const room = payload[entry.room] || {};
const isOn = !!(room['l_' + entry.light] || room['l' + entry.light]);
const flowKey = 'nvlToHa_' + entry.entityId.replace(/\./g, '_');
const last = flow.get(flowKey);
if (last === isOn) continue;
flow.set(flowKey, isOn);
const domain = entry.entityId.split('.')[0] || 'input_boolean';
const action = domain + '.' + (isOn ? 'turn_on' : 'turn_off');
const out = { payload: { action: action, target: { entity_id: [entry.entityId] } } };
if (isOn) onMsgs.push(out); else offMsgs.push(out);
}
if (onMsgs.length === 0 && offMsgs.length === 0) return null;
return [onMsgs, offMsgs];

View File

@@ -0,0 +1,30 @@
// Build the payload for nvl-send from flow.nvlInState.
// Redesign: all rooms use struct_room_cmds. Add more names to roomNames when you add rooms.
// Wire: HA to NVL / Zigbee to NVL → this function → nvl-send → udp out
const struct_room_cmds_default = {
ha_l1_on: false, ha_l1_off: false, ha_l2_on: false, ha_l2_off: false,
ha_l3_on: false, ha_l3_off: false, ha_l4_on: false, ha_l4_off: false,
ha_l5_on: false, ha_l5_off: false, ha_l6_on: false, ha_l6_off: false,
zigbee_sw1: false, zigbee_sw2: false, zigbee_sw3: false,
zigbee_sw4: false, zigbee_sw5: false, zigbee_sw6: false,
ha_all_on: false, ha_all_off: false
};
// NVL variable names per room (struct_room_cmds). Add more when you add rooms.
const roomNames = ['cmd_livingroom'];
const state = flow.get('nvlInState') || { rooms: {}, boiler: {} };
const rooms = state.rooms || {};
const payload = {};
for (const name of roomNames) {
const cmd = rooms[name] || {};
payload[name] = { ...struct_room_cmds_default };
for (const k of Object.keys(struct_room_cmds_default)) {
if (cmd[k] !== undefined) payload[name][k] = !!cmd[k];
}
}
msg.payload = payload;
return msg;

View File

@@ -0,0 +1,32 @@
// Writes to state.rooms.cmd_livingroom (same as HA to NVL). Zigbee action → zigbee_sw1..6.
const ROOM_NAME = 'cmd_livingroom';
const actionToSwitch = { single: 1, double: 2, hold: 3, release: 4, triple: 5, quad: 6 };
const payload = msg.payload || {};
const actionRaw = (payload.action || payload.click || '').toLowerCase();
// Support "1_single", "2_double" etc. (button_number + action) or plain "single", "double"
const parts = actionRaw.split('_');
let swIndex = null;
if (parts.length >= 2) {
const buttonNum = parseInt(parts[0], 10);
if (buttonNum >= 1 && buttonNum <= 6) swIndex = buttonNum;
}
if (swIndex == null) swIndex = actionToSwitch[actionRaw];
if (swIndex == null && parts.length >= 2) swIndex = actionToSwitch[parts.slice(1).join('_')];
if (swIndex == null) swIndex = actionToSwitch[parts[parts.length - 1]];
if (swIndex == null) {
node.warn('[Zigbee to NVL] unknown action: ' + JSON.stringify(actionRaw) + ' payload=' + JSON.stringify(payload));
return null;
}
if (!flow.get('nvlInState')) flow.set('nvlInState', { rooms: {}, boiler: {} });
const state = flow.get('nvlInState');
if (!state.rooms[ROOM_NAME]) state.rooms[ROOM_NAME] = {};
state.rooms[ROOM_NAME]['zigbee_sw' + swIndex] = true;
flow.set('nvlInState', state);
node.warn('[Zigbee to NVL] cmd_livingroom zigbee_sw' + swIndex + '=true (action=' + actionRaw + '), buildAndSend + zigbeeClear');
msg.payload = { buildAndSend: true };
msg.zigbeeClear = { room: ROOM_NAME, key: 'zigbee_sw' + swIndex };
return msg;

View File

@@ -1,89 +0,0 @@
# fb_switch Analysis Summary
## Quick Assessment
**Current Approach**: ⚠️ **Needs Improvement**
**Main Issue**: Toggle-based control causes state desynchronization with Home Assistant.
## Key Problems
### 1. State Desynchronization
- **Problem**: HA sends ON/OFF commands, but CODESYS toggles
- **Result**: HA thinks light is ON, sends ON again → CODESYS toggles it OFF
- **Impact**: Lights don't match HA state
### 2. No Status Feedback
- **Problem**: Status comes from toggle button, not actual relay
- **Result**: Can't detect relay hardware failures
- **Impact**: HA may show wrong status
### 3. Mixed Control Sources
- **Problem**: HA and Zigbee both use same toggle mechanism
- **Result**: Conflicting behaviors
- **Impact**: Unpredictable control
## What You Need
**Control from Home Assistant** (ON/OFF switches)
**Control from Zigbee switches** (toggle behavior)
**Status feedback** (actual relay state)
## Recommended Solution
### Option 1: Full Redesign (Recommended)
- Separate ON/OFF commands for HA
- Edge detection for Zigbee (toggle)
- Read actual relay status from EtherCAT
- Clear priority system
**Pros**: Clean, maintainable, extensible
**Cons**: Requires more changes
### Option 2: Simplified Improvement
- Add ON/OFF commands alongside toggle
- Keep most existing structure
- Add status feedback
**Pros**: Less changes, faster to implement
**Cons**: Less clean, still some complexity
## Quick Fix (Minimal Changes)
If you need a quick fix while planning the redesign:
1. **In Node-RED**: Convert HA ON/OFF to edge pulses
```javascript
// Only send toggle on state change
if (currentState !== newState) {
sendToggle = true;
}
```
2. **Add status readback**: Read actual EtherCAT output state
```iec
lights.l_1_status := EtherCAT_Output_Channel_1;
```
3. **Update HA**: Use state topic for status feedback
## Decision Matrix
| Approach | Complexity | Time | Maintainability | Recommended For |
|----------|-----------|------|-----------------|-----------------|
| Full Redesign | High | 2-3 days | Excellent | Long-term solution |
| Simplified | Medium | 1 day | Good | Quick improvement |
| Quick Fix | Low | 2-4 hours | Fair | Temporary solution |
## My Recommendation
**Start with Quick Fix****Then implement Simplified****Plan Full Redesign**
This gives you:
1. Immediate working solution
2. Better solution in short term
3. Best solution for long term
---
**See**: [Full Redesign Recommendation](fb_switch-redesign-recommendation.md) for detailed implementation.

View File

@@ -1,223 +0,0 @@
# fb_switch Function Block Documentation
## Overview
The `fb_switch` function block is the core control logic for managing lighting in each room. It processes switch inputs from Node-RED and generates light control outputs.
## Function Block Structure
```iec
FUNCTION_BLOCK fb_switch
VAR_INPUT
switches: struct_switches; // Switch states received from Node-RED
END_VAR
VAR_OUTPUT
lights: struct_lights; // Light control outputs sent to Node-RED
END_VAR
VAR
all_off: fb_toogleButton; // Toggle button for "all off" command
all_on: fb_toogleButton; // Toggle button for "all on" command
sw_1: fb_toogleButton; // Toggle button for switch 1
sw_2: fb_toogleButton; // Toggle button for switch 2
sw_3: fb_toogleButton; // Toggle button for switch 3
sw_4: fb_toogleButton; // Toggle button for switch 4
sw_5: fb_toogleButton; // Toggle button for switch 5
sw_6: fb_toogleButton; // Toggle button for switch 6
END_VAR
```
## Data Structures
### struct_switches (Input)
```iec
TYPE struct_switches :
STRUCT
all_off: BOOL; // Command to turn all lights off
all_on: BOOL; // Command to turn all lights on
sw_1: BOOL; // Switch 1 state
sw_2: BOOL; // Switch 2 state
sw_3: BOOL; // Switch 3 state
sw_4: BOOL; // Switch 4 state
sw_5: BOOL; // Switch 5 state
sw_6: BOOL; // Switch 6 state
END_STRUCT
END_TYPE
```
### struct_lights (Output)
```iec
TYPE struct_lights:
STRUCT
l_1: BOOL; // Light 1 control output
l_2: BOOL; // Light 2 control output
l_3: BOOL; // Light 3 control output
l_4: BOOL; // Light 4 control output
l_5: BOOL; // Light 5 control output
l_6: BOOL; // Light 6 control output
END_STRUCT
END_TYPE
```
## Functionality
### Individual Switch Control
Each switch (`sw_1` through `sw_6`) is processed by a `fb_toogleButton` instance:
- **Input**: Switch state from `switches.sw_X` (received from Node-RED)
- **Output**: Light control to `lights.l_X` (sent to Node-RED)
- **Behavior**: Toggle functionality - each switch press toggles the corresponding light state
### Global Commands
1. **All Off (`switches.all_off`)**:
- When activated, turns off all lights (l_1 through l_6)
- Uses `fb_toogleButton` instance `all_off`
2. **All On (`switches.all_on`)**:
- When activated, turns on all lights (l_1 through l_6)
- Uses `fb_toogleButton` instance `all_on`
- Logic uses AND gates to combine with individual switch states
### Logic Flow
1. **Switch Input Processing**:
- Each `sw_X` input is fed to its corresponding `fb_toogleButton` instance
- The toggle button processes the input and generates an output
2. **Light Output Generation**:
- Individual switch outputs directly control their corresponding lights
- `all_off` command can override individual controls
- `all_on` command combines with individual switch states using AND logic
3. **Output Assignment**:
- `sw_1``lights.l_1`
- `sw_2``lights.l_2`
- `sw_3``lights.l_3`
- `sw_4``lights.l_4`
- `sw_5``lights.l_5`
- `sw_6``lights.l_6`
## fb_toogleButton Function Block
The `fb_toogleButton` is a toggle button implementation with debouncing and timing features.
### Structure
```iec
FUNCTION_BLOCK fb_toogleButton
VAR_INPUT
Input: BOOL; // Input signal (switch state)
END_VAR
VAR_OUTPUT
Output: BOOL; // Output signal (light control)
END_VAR
VAR
Switch: BOOL; // Internal switch state
Light: BOOL; // Internal light state
Flag1: BOOL; // Internal flag 1
Flag2: BOOL; // Internal flag 2
RT: R_TRIG; // Rising edge trigger
Pulse: TP; // Pulse timer
CTU_0: CTU; // Counter up 0
CTD_0: CTD; // Counter down 0
CTU_1: CTU; // Counter up 1
CTU_2: CTU; // Counter up 2
TOF_0: TOF; // Timer off delay 0
END_VAR
```
### Features
- **Rising Edge Detection**: Uses `R_TRIG` to detect switch press events
- **Pulse Generation**: Uses `TP` (pulse timer) for debouncing or pulse generation
- **Timing Control**: Uses `TOF` (timer off delay) for delayed turn-off
- **Counters**: Uses `CTU` and `CTD` counters for state management
- **Toggle Logic**: Implements toggle behavior - each press changes state
### Behavior
1. **Input Processing**:
- Detects rising edge of input signal
- Generates a pulse for processing
- Applies timing delays for debouncing
2. **State Management**:
- Maintains internal switch and light states
- Toggles output on each valid input pulse
- Uses flags for state tracking
3. **Output Generation**:
- Output reflects the toggled state
- May include timing delays (TOF) for smooth operation
## Usage in System
### Per-Room Instance
Each room has an instance of `fb_switch`:
- `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`
### Network Variable Integration
- **Input**: Receives `struct_switches` from Node-RED via network variable `NVL_Receiver`
- **Output**: Sends `struct_lights` to Node-RED via network variable `NVL_Sender`
### Control Flow
```
Node-RED (Zigbee Switch)
MQTT Message
Node-RED Processing
Network Variable (struct_switches)
fb_switch (CODESYS)
fb_toogleButton Processing
Light Control Output (struct_lights)
Network Variable (NVL_Sender)
Node-RED
EtherCAT Relay Output
Physical Light
```
## Notes
- Each room can control up to 6 individual lights
- Global commands (`all_on`, `all_off`) provide room-level control
- Toggle button implementation includes debouncing to prevent rapid switching
- Timing functions ensure smooth operation and prevent electrical issues
- The system supports both individual light control and group control per room
---
**Documentation Date**: January 27, 2026
**Sources**:
- `Home_Automation.export` (CODESYS XML)
- `Home_Automation.xml` (PLCopen XML)
**Additional Details**: See [CODESYS XML Analysis](../codesys/codesys-xml-analysis.md) for timing characteristics and detailed logic flow extracted from the PLCopen XML format.

View File

@@ -1,451 +0,0 @@
# fb_switch Redesign Recommendation
## Current Implementation Issues
### Problem 1: Toggle-Based Control
**Issue**: The current `fb_toogleButton` implementation uses toggle logic, which causes state desynchronization with Home Assistant.
**Why this is a problem**:
- Home Assistant sends explicit ON/OFF commands
- CODESYS treats every input as a toggle trigger
- If HA thinks light is ON and sends ON again, CODESYS toggles it OFF
- State becomes out of sync between HA and actual relay state
**Example Scenario**:
1. HA sends `sw_1 = TRUE` (turn ON)
2. CODESYS toggles: OFF → ON ✓
3. HA sends `sw_1 = TRUE` again (thinking it's already ON)
4. CODESYS toggles: ON → OFF ✗ (wrong!)
### Problem 2: No Direct Set/Reset Commands
**Issue**: Current structure only has toggle triggers, no explicit ON/OFF commands.
**Current Structure**:
```iec
struct_switches:
sw_1: BOOL; // Treated as toggle trigger
all_on: BOOL; // Toggle trigger
all_off: BOOL; // Toggle trigger
```
**What's needed**:
- Separate ON and OFF commands
- Edge detection for Zigbee switches (toggle behavior)
- Direct set/reset for Home Assistant
### Problem 3: Status Feedback Limitations
**Issue**: Current `lights.l_X` outputs represent toggle button state, not actual relay status.
**Problems**:
- No direct feedback from physical relay outputs
- Status may not match actual relay state if relay fails
- No way to detect relay hardware failures
## Recommended Redesign
### New Data Structure
```iec
TYPE struct_switches :
STRUCT
// Home Assistant Commands (set/reset)
ha_l1_on: BOOL; // HA command: Light 1 ON
ha_l1_off: BOOL; // HA command: Light 1 OFF
ha_l2_on: BOOL;
ha_l2_off: BOOL;
ha_l3_on: BOOL;
ha_l3_off: BOOL;
ha_l4_on: BOOL;
ha_l4_off: BOOL;
ha_l5_on: BOOL;
ha_l5_off: BOOL;
ha_l6_on: BOOL;
ha_l6_off: BOOL;
// Zigbee Switch Inputs (edge detection for toggle)
zigbee_sw1: BOOL; // Zigbee switch 1 (edge detected)
zigbee_sw2: BOOL;
zigbee_sw3: BOOL;
zigbee_sw4: BOOL;
zigbee_sw5: BOOL;
zigbee_sw6: BOOL;
// Global Commands
ha_all_on: BOOL; // HA: All lights ON
ha_all_off: BOOL; // HA: All lights OFF
END_STRUCT
END_TYPE
TYPE struct_lights:
STRUCT
l_1: BOOL; // Light 1 control output
l_2: BOOL; // Light 2 control output
l_3: BOOL; // Light 3 control output
l_4: BOOL; // Light 4 control output
l_5: BOOL; // Light 5 control output
l_6: BOOL; // Light 6 control output
// Status feedback (read from actual relay outputs)
l_1_status: BOOL; // Actual relay 1 state
l_2_status: BOOL; // Actual relay 2 state
l_3_status: BOOL; // Actual relay 3 state
l_4_status: BOOL; // Actual relay 4 state
l_5_status: BOOL; // Actual relay 5 state
l_6_status: BOOL; // Actual relay 6 state
END_STRUCT
END_TYPE
```
### New Function Block: fb_lightControl
```iec
FUNCTION_BLOCK fb_lightControl
VAR_INPUT
// Home Assistant Commands
ha_on: BOOL; // HA command: Turn ON
ha_off: BOOL; // HA command: Turn OFF
// Zigbee Switch Input
zigbee_sw: BOOL; // Zigbee switch (edge detected)
// Status Feedback
relay_status: BOOL; // Actual relay state (read from EtherCAT)
END_VAR
VAR_OUTPUT
light_output: BOOL; // Control output to relay
light_status: BOOL; // Status to send back (actual relay state)
END_VAR
VAR
// Edge detection for Zigbee
zigbee_prev: BOOL;
zigbee_edge: BOOL;
// Command processing
ha_on_prev: BOOL;
ha_off_prev: BOOL;
ha_on_edge: BOOL;
ha_off_edge: BOOL;
// Internal state
light_state: BOOL;
// Edge triggers
r_trig_ha_on: R_TRIG;
r_trig_ha_off: R_TRIG;
r_trig_zigbee: R_TRIG;
END_VAR
```
### Implementation Logic
**Priority Order** (highest to lowest):
1. **HA OFF command** - Always turns light OFF
2. **HA ON command** - Always turns light ON
3. **Zigbee toggle** - Toggles current state
4. **Maintain current state** - If no commands
**Logic Flow**:
```iec
// Edge detection for commands
r_trig_ha_on(CLK := ha_on);
ha_on_edge := r_trig_ha_on.Q;
r_trig_ha_off(CLK := ha_off);
ha_off_edge := r_trig_ha_off.Q;
r_trig_zigbee(CLK := zigbee_sw);
zigbee_edge := r_trig_zigbee.Q;
// State machine
IF ha_off_edge THEN
light_state := FALSE;
ELSIF ha_on_edge THEN
light_state := TRUE;
ELSIF zigbee_edge THEN
light_state := NOT light_state;
END_IF
// Output assignment
light_output := light_state;
// Status feedback (read from actual relay)
light_status := relay_status;
```
### New fb_switch Implementation
```iec
FUNCTION_BLOCK fb_switch
VAR_INPUT
switches: struct_switches;
relay_status: struct_lights; // Read from EtherCAT outputs
END_VAR
VAR_OUTPUT
lights: struct_lights;
END_VAR
VAR
l1: fb_lightControl;
l2: fb_lightControl;
l3: fb_lightControl;
l4: fb_lightControl;
l5: fb_lightControl;
l6: fb_lightControl;
END_VAR
// Light 1 Control
l1(
ha_on := switches.ha_l1_on,
ha_off := switches.ha_l1_off,
zigbee_sw := switches.zigbee_sw1,
relay_status := relay_status.l_1_status
);
lights.l_1 := l1.light_output;
lights.l_1_status := l1.light_status;
// Light 2 Control
l2(
ha_on := switches.ha_l2_on,
ha_off := switches.ha_l2_off,
zigbee_sw := switches.zigbee_sw2,
relay_status := relay_status.l_2_status
);
lights.l_2 := l2.light_output;
lights.l_2_status := l2.light_status;
// ... (repeat for l3-l6)
// Global Commands
IF switches.ha_all_off THEN
lights.l_1 := FALSE;
lights.l_2 := FALSE;
lights.l_3 := FALSE;
lights.l_4 := FALSE;
lights.l_5 := FALSE;
lights.l_6 := FALSE;
ELSIF switches.ha_all_on THEN
lights.l_1 := TRUE;
lights.l_2 := TRUE;
lights.l_3 := TRUE;
lights.l_4 := TRUE;
lights.l_5 := TRUE;
lights.l_6 := TRUE;
END_IF;
```
## Node-RED Integration Changes
### Current Flow (Toggle-Based)
```
Zigbee Switch → MQTT → Node-RED → CODESYS (sw_1 = TRUE) → Toggle
Home Assistant → MQTT → Node-RED → CODESYS (sw_1 = TRUE) → Toggle (WRONG!)
```
### New Flow (Command-Based)
```
Zigbee Switch → MQTT → Node-RED → CODESYS (zigbee_sw1 = edge) → Toggle
Home Assistant → MQTT → Node-RED → CODESYS (ha_l1_on = TRUE) → Set ON
Home Assistant → MQTT → Node-RED → CODESYS (ha_l1_off = TRUE) → Set OFF
```
### Node-RED Flow Example
**For Zigbee Switches**:
```javascript
// Detect button press (edge detection)
if (msg.payload.action === "single") {
// Send edge trigger to CODESYS
msg.payload = {
zigbee_sw1: true // Edge detected
};
return msg;
}
```
**For Home Assistant**:
```javascript
// Direct ON/OFF commands
if (msg.payload === "ON") {
msg.payload = {
ha_l1_on: true,
ha_l1_off: false
};
} else if (msg.payload === "OFF") {
msg.payload = {
ha_l1_on: false,
ha_l1_off: true
};
}
return msg;
```
**Status Feedback to Home Assistant**:
```javascript
// Read status from CODESYS
const status = msg.payload.l_1_status;
// Publish to HA
msg.topic = "homeassistant/light/room1_light1/state";
msg.payload = status ? "ON" : "OFF";
return msg;
```
## Home Assistant Configuration
### MQTT Light Entity
```yaml
light:
- platform: mqtt
name: "Room 1 Light 1"
state_topic: "homeassistant/light/room1_light1/state"
command_topic: "homeassistant/light/room1_light1/set"
state_value_template: "{{ value_json.state }}"
command_on_template: '{"state":"ON"}'
command_off_template: '{"state":"OFF"}'
qos: 1
retain: true
```
### Node-RED Flow for HA Integration
```
[HA Command] → [Parse] → [Convert to CODESYS format] → [Network Variable]
[CODESYS Status] → [Network Variable] → [Convert to HA format] → [HA State]
```
## Benefits of Redesign
### 1. State Synchronization
- ✅ HA always knows actual relay state
- ✅ No state desynchronization
- ✅ Reliable ON/OFF commands
### 2. Multiple Control Sources
- ✅ HA uses set/reset commands
- ✅ Zigbee switches use toggle (edge detection)
- ✅ Both work simultaneously without conflicts
### 3. Status Feedback
- ✅ Actual relay state read from EtherCAT
- ✅ Hardware failure detection possible
- ✅ Accurate status in Home Assistant
### 4. Better Debugging
- ✅ Clear separation of commands
- ✅ Can trace which source triggered action
- ✅ Easier troubleshooting
### 5. Extensibility
- ✅ Easy to add more control sources
- ✅ Can add priority levels
- ✅ Can add scheduling/automation
## Migration Path
### Step 1: Update Data Structures
1. Modify `struct_switches` to include HA commands and Zigbee inputs
2. Modify `struct_lights` to include status feedback
3. Keep old structure temporarily for backward compatibility
### Step 2: Create New Function Blocks
1. Implement `fb_lightControl` with new logic
2. Update `fb_switch` to use new function blocks
3. Test in simulation first
### Step 3: Update Node-RED Flows
1. Modify flows to send HA commands (ON/OFF)
2. Keep Zigbee flows with edge detection
3. Add status feedback flows
### Step 4: Update Home Assistant
1. Configure MQTT light entities
2. Test control from HA
3. Verify status updates
### Step 5: Testing
1. Test HA control (ON/OFF)
2. Test Zigbee switch control (toggle)
3. Test status feedback
4. Test both simultaneously
## Alternative: Simpler Approach (If Full Redesign Too Complex)
If the full redesign is too complex, here's a simpler improvement:
### Simplified Improvement
**Keep toggle logic but add state tracking**:
```iec
FUNCTION_BLOCK fb_lightControl_Simple
VAR_INPUT
toggle: BOOL; // Toggle command (edge detected)
set_on: BOOL; // Set ON command (edge detected)
set_off: BOOL; // Set OFF command (edge detected)
relay_status: BOOL; // Actual relay state
END_VAR
VAR_OUTPUT
output: BOOL;
status: BOOL;
END_VAR
VAR
state: BOOL;
r_trig_toggle: R_TRIG;
r_trig_on: R_TRIG;
r_trig_off: R_TRIG;
END_VAR
// Edge detection
r_trig_toggle(CLK := toggle);
r_trig_on(CLK := set_on);
r_trig_off(CLK := set_off);
// State machine
IF r_trig_off.Q THEN
state := FALSE;
ELSIF r_trig_on.Q THEN
state := TRUE;
ELSIF r_trig_toggle.Q THEN
state := NOT state;
END_IF;
output := state;
status := relay_status; // Read from actual relay
```
**Data Structure**:
```iec
struct_switches:
sw1_toggle: BOOL; // Zigbee toggle
sw1_on: BOOL; // HA ON command
sw1_off: BOOL; // HA OFF command
// ... repeat for sw2-sw6
```
This simpler approach:
- ✅ Keeps existing structure mostly intact
- ✅ Adds explicit ON/OFF commands for HA
- ✅ Maintains toggle for Zigbee
- ✅ Adds status feedback
- ⚠️ Still requires data structure changes
- ⚠️ Less clean than full redesign
## Recommendation
**I recommend the full redesign** because:
1. Better long-term maintainability
2. Clear separation of concerns
3. Easier to extend and debug
4. Proper state management
5. Industry-standard approach
However, if time is limited, the **simplified approach** is a good compromise that addresses the main issues.
---
**Next Steps**:
1. Review this recommendation
2. Decide on full redesign vs. simplified approach
3. Plan migration strategy
4. Update Node-RED flows accordingly
5. Test thoroughly before deploying

View File

@@ -1,873 +0,0 @@
# Light Naming Configuration - Hybrid Structure
## Overview
This document defines the naming convention and structure for all lights in the home automation system using the Hybrid Structure (Option 4). This will serve as the basis for the redesign.
**Structure**: Room-organized in CODESYS, flattened for Home Assistant
**Naming Convention**: `{room}_{function}` (e.g., `kitchen_main`, `master_bedroom_bedside_left`)
---
## Room-by-Room Light Configuration
### 1. Master Bedroom
**Room ID**: `master_bedroom`
**Display Name**: "Master Bedroom"
**Lights**:
- `main` - Main ceiling light
- `bedside_left` - Left bedside lamp
- `bedside_right` - Right bedside lamp
- `closet` - Closet light
- `reading` - Reading light (optional)
- `ambient` - Ambient/atmospheric lighting (optional)
**CODESYS Structure**:
```iec
rooms.master_bedroom:
main: struct_light_control;
bedside_left: struct_light_control;
bedside_right: struct_light_control;
closet: struct_light_control;
reading: struct_light_control; // Optional
ambient: struct_light_control; // Optional
```
**Home Assistant Entities**:
- `light.master_bedroom_main`
- `light.master_bedroom_bedside_left`
- `light.master_bedroom_bedside_right`
- `light.master_bedroom_closet`
- `light.master_bedroom_reading` (if used)
- `light.master_bedroom_ambient` (if used)
---
### 2. Master Bathroom
**Room ID**: `master_bathroom`
**Display Name**: "Master Bathroom"
**Lights**:
- `main` - Main ceiling light
- `mirror` - Mirror/vanity light
- `shower` - Shower light
- `night_light` - Night light (low brightness)
**CODESYS Structure**:
```iec
rooms.master_bathroom:
main: struct_light_control;
mirror: struct_light_control;
shower: struct_light_control;
night_light: struct_light_control;
```
**Home Assistant Entities**:
- `light.master_bathroom_main`
- `light.master_bathroom_mirror`
- `light.master_bathroom_shower`
- `light.master_bathroom_night_light`
---
### 3. Bedroom 1
**Room ID**: `bedroom_1`
**Display Name**: "Bedroom 1"
**Lights**:
- `main` - Main ceiling light
- `bedside` - Bedside lamp
- `desk` - Desk lamp (if applicable)
- `closet` - Closet light (if applicable)
**CODESYS Structure**:
```iec
rooms.bedroom_1:
main: struct_light_control;
bedside: struct_light_control;
desk: struct_light_control; // Optional
closet: struct_light_control; // Optional
```
**Home Assistant Entities**:
- `light.bedroom_1_main`
- `light.bedroom_1_bedside`
- `light.bedroom_1_desk` (if used)
- `light.bedroom_1_closet` (if used)
---
### 4. Bedroom 2
**Room ID**: `bedroom_2`
**Display Name**: "Bedroom 2"
**Lights**:
- `main` - Main ceiling light
- `bedside` - Bedside lamp
- `desk` - Desk lamp (if applicable)
- `closet` - Closet light (if applicable)
**CODESYS Structure**:
```iec
rooms.bedroom_2:
main: struct_light_control;
bedside: struct_light_control;
desk: struct_light_control; // Optional
closet: struct_light_control; // Optional
```
**Home Assistant Entities**:
- `light.bedroom_2_main`
- `light.bedroom_2_bedside`
- `light.bedroom_2_desk` (if used)
- `light.bedroom_2_closet` (if used)
---
### 5. Bathroom
**Room ID**: `bathroom`
**Display Name**: "Bathroom"
**Lights**:
- `main` - Main ceiling light
- `mirror` - Mirror/vanity light
- `night_light` - Night light
**CODESYS Structure**:
```iec
rooms.bathroom:
main: struct_light_control;
mirror: struct_light_control;
night_light: struct_light_control;
```
**Home Assistant Entities**:
- `light.bathroom_main`
- `light.bathroom_mirror`
- `light.bathroom_night_light`
---
### 6. Kitchen
**Room ID**: `kitchen`
**Display Name**: "Kitchen"
**Lights**:
- `main` - Main ceiling light
- `under_cabinet` - Under cabinet task lighting
- `island` - Island pendant light
- `pantry` - Pantry light (if separate from pantry room)
- `sink` - Sink area light
**CODESYS Structure**:
```iec
rooms.kitchen:
main: struct_light_control;
under_cabinet: struct_light_control;
island: struct_light_control;
pantry: struct_light_control; // If in kitchen
sink: struct_light_control;
```
**Home Assistant Entities**:
- `light.kitchen_main`
- `light.kitchen_under_cabinet`
- `light.kitchen_island`
- `light.kitchen_pantry` (if used)
- `light.kitchen_sink`
---
### 7. Living Room
**Room ID**: `living_room`
**Display Name**: "Living Room"
**Lights**:
- `main` - Main ceiling light
- `reading` - Reading lamp
- `tv` - TV area lighting
- `accent` - Accent lighting
- `fireplace` - Fireplace area lighting (if applicable)
**CODESYS Structure**:
```iec
rooms.living_room:
main: struct_light_control;
reading: struct_light_control;
tv: struct_light_control;
accent: struct_light_control;
fireplace: struct_light_control; // Optional
```
**Home Assistant Entities**:
- `light.living_room_main`
- `light.living_room_reading`
- `light.living_room_tv`
- `light.living_room_accent`
- `light.living_room_fireplace` (if used)
---
### 8. Dining Room
**Room ID**: `dining_room`
**Display Name**: "Dining Room"
**Lights**:
- `main` - Main chandelier/ceiling light
- `ambient` - Ambient lighting
- `buffet` - Buffet/sideboard lighting (if applicable)
**CODESYS Structure**:
```iec
rooms.dining_room:
main: struct_light_control;
ambient: struct_light_control;
buffet: struct_light_control; // Optional
```
**Home Assistant Entities**:
- `light.dining_room_main`
- `light.dining_room_ambient`
- `light.dining_room_buffet` (if used)
---
### 9. Hallway
**Room ID**: `hallway`
**Display Name**: "Hallway"
**Lights**:
- `main` - Main hallway light
- `secondary` - Secondary hallway light (if long hallway)
- `night_light` - Night light
**CODESYS Structure**:
```iec
rooms.hallway:
main: struct_light_control;
secondary: struct_light_control; // Optional
night_light: struct_light_control;
```
**Home Assistant Entities**:
- `light.hallway_main`
- `light.hallway_secondary` (if used)
- `light.hallway_night_light`
---
### 10. Pantry
**Room ID**: `pantry`
**Display Name**: "Pantry"
**Lights**:
- `main` - Main pantry light
**CODESYS Structure**:
```iec
rooms.pantry:
main: struct_light_control;
```
**Home Assistant Entities**:
- `light.pantry_main`
---
### 11. Entrance
**Room ID**: `entrance`
**Display Name**: "Entrance"
**Lights**:
- `main` - Main entrance light
- `security` - Security/porch light
**CODESYS Structure**:
```iec
rooms.entrance:
main: struct_light_control;
security: struct_light_control;
```
**Home Assistant Entities**:
- `light.entrance_main`
- `light.entrance_security`
---
### 12. Guest WC
**Room ID**: `guest_wc`
**Display Name**: "Guest WC"
**Lights**:
- `main` - Main light
- `night_light` - Night light
**CODESYS Structure**:
```iec
rooms.guest_wc:
main: struct_light_control;
night_light: struct_light_control;
```
**Home Assistant Entities**:
- `light.guest_wc_main`
- `light.guest_wc_night_light`
---
### 13. Outdoor Veranda
**Room ID**: `outdoor_veranda`
**Display Name**: "Outdoor Veranda"
**Lights**:
- `main` - Main veranda light
- `ambient` - Ambient/atmospheric lighting
- `security` - Security lighting
**CODESYS Structure**:
```iec
rooms.outdoor_veranda:
main: struct_light_control;
ambient: struct_light_control;
security: struct_light_control;
```
**Home Assistant Entities**:
- `light.outdoor_veranda_main`
- `light.outdoor_veranda_ambient`
- `light.outdoor_veranda_security`
---
### 14. Outdoor Front
**Room ID**: `outdoor_front`
**Display Name**: "Outdoor Front"
**Lights**:
- `porch` - Front porch light
- `security` - Security light
- `pathway` - Pathway lighting
**CODESYS Structure**:
```iec
rooms.outdoor_front:
porch: struct_light_control;
security: struct_light_control;
pathway: struct_light_control;
```
**Home Assistant Entities**:
- `light.outdoor_front_porch`
- `light.outdoor_front_security`
- `light.outdoor_front_pathway`
---
### 15. Outdoor Side
**Room ID**: `outdoor_side`
**Display Name**: "Outdoor Side"
**Lights**:
- `security` - Side security light
- `pathway` - Side pathway lighting
**CODESYS Structure**:
```iec
rooms.outdoor_side:
security: struct_light_control;
pathway: struct_light_control;
```
**Home Assistant Entities**:
- `light.outdoor_side_security`
- `light.outdoor_side_pathway`
---
### 16. Outdoor Back
**Room ID**: `outdoor_back`
**Display Name**: "Outdoor Back"
**Lights**:
- `patio` - Patio/backyard light
- `security` - Back security light
- `garden` - Garden lighting (if applicable)
**CODESYS Structure**:
```iec
rooms.outdoor_back:
patio: struct_light_control;
security: struct_light_control;
garden: struct_light_control; // Optional
```
**Home Assistant Entities**:
- `light.outdoor_back_patio`
- `light.outdoor_back_security`
- `light.outdoor_back_garden` (if used)
---
## Complete CODESYS Structure Definition
```iec
TYPE struct_light_control :
STRUCT
// Control Commands
ha_on: BOOL; // Home Assistant: Turn ON
ha_off: BOOL; // Home Assistant: Turn OFF
zigbee_toggle: BOOL; // Zigbee Switch: Toggle (edge detected)
// Status
state: BOOL; // Current light state
relay_status: BOOL; // Actual relay status (read from EtherCAT)
END_STRUCT
END_TYPE
TYPE struct_room_lights :
STRUCT
main: struct_light_control;
secondary: struct_light_control;
task: struct_light_control;
ambient: struct_light_control;
accent: struct_light_control;
security: struct_light_control;
// Room-specific lights defined per room
END_STRUCT
END_TYPE
VAR_GLOBAL
rooms: STRUCT
master_bedroom: STRUCT
main: struct_light_control;
bedside_left: struct_light_control;
bedside_right: struct_light_control;
closet: struct_light_control;
reading: struct_light_control;
ambient: struct_light_control;
END_STRUCT;
master_bathroom: STRUCT
main: struct_light_control;
mirror: struct_light_control;
shower: struct_light_control;
night_light: struct_light_control;
END_STRUCT;
bedroom_1: STRUCT
main: struct_light_control;
bedside: struct_light_control;
desk: struct_light_control;
closet: struct_light_control;
END_STRUCT;
bedroom_2: STRUCT
main: struct_light_control;
bedside: struct_light_control;
desk: struct_light_control;
closet: struct_light_control;
END_STRUCT;
bathroom: STRUCT
main: struct_light_control;
mirror: struct_light_control;
night_light: struct_light_control;
END_STRUCT;
kitchen: STRUCT
main: struct_light_control;
under_cabinet: struct_light_control;
island: struct_light_control;
pantry: struct_light_control;
sink: struct_light_control;
END_STRUCT;
living_room: STRUCT
main: struct_light_control;
reading: struct_light_control;
tv: struct_light_control;
accent: struct_light_control;
fireplace: struct_light_control;
END_STRUCT;
dining_room: STRUCT
main: struct_light_control;
ambient: struct_light_control;
buffet: struct_light_control;
END_STRUCT;
hallway: STRUCT
main: struct_light_control;
secondary: struct_light_control;
night_light: struct_light_control;
END_STRUCT;
pantry: STRUCT
main: struct_light_control;
END_STRUCT;
entrance: STRUCT
main: struct_light_control;
security: struct_light_control;
END_STRUCT;
guest_wc: STRUCT
main: struct_light_control;
night_light: struct_light_control;
END_STRUCT;
outdoor_veranda: STRUCT
main: struct_light_control;
ambient: struct_light_control;
security: struct_light_control;
END_STRUCT;
outdoor_front: STRUCT
porch: struct_light_control;
security: struct_light_control;
pathway: struct_light_control;
END_STRUCT;
outdoor_side: STRUCT
security: struct_light_control;
pathway: struct_light_control;
END_STRUCT;
outdoor_back: STRUCT
patio: struct_light_control;
security: struct_light_control;
garden: struct_light_control;
END_STRUCT;
END_STRUCT;
END_VAR
```
---
## Light Function Types Reference
Use these standard function names for consistency:
| Function | Description | Typical Use |
|----------|-------------|-------------|
| `main` | Primary/ceiling light | Main illumination in room |
| `secondary` | Secondary light | Additional main light |
| `bedside_left` | Left bedside lamp | Bedroom bedside |
| `bedside_right` | Right bedside lamp | Bedroom bedside |
| `bedside` | Single bedside lamp | Single bed rooms |
| `closet` | Closet light | Storage areas |
| `desk` | Desk lamp | Study/work areas |
| `reading` | Reading light | Reading areas |
| `mirror` | Mirror/vanity light | Bathrooms |
| `shower` | Shower light | Bathroom shower |
| `night_light` | Night light | Low brightness |
| `under_cabinet` | Under cabinet | Kitchen task lighting |
| `island` | Island pendant | Kitchen island |
| `sink` | Sink area light | Kitchen/bathroom |
| `tv` | TV area lighting | Living room |
| `accent` | Accent lighting | Decorative |
| `ambient` | Ambient lighting | Atmospheric |
| `fireplace` | Fireplace area | Living room |
| `buffet` | Buffet/sideboard | Dining room |
| `porch` | Porch light | Entrance |
| `patio` | Patio light | Backyard |
| `security` | Security light | Outdoor security |
| `pathway` | Pathway lighting | Outdoor paths |
| `garden` | Garden lighting | Outdoor garden |
---
## Water Boiler Control
### Water Boiler Structure
```iec
TYPE struct_water_boiler :
STRUCT
// Control Commands
ha_on: BOOL; // Home Assistant: Turn ON
ha_off: BOOL; // Home Assistant: Turn OFF
schedule_on: BOOL; // Scheduled ON (from automation)
schedule_off: BOOL; // Scheduled OFF (from automation)
// Status
state: BOOL; // Current boiler state (ON/OFF)
relay_status: BOOL; // Actual relay status
temperature: REAL; // Water temperature (if sensor available)
// Safety
safety_override: BOOL; // Safety system override (read-only)
error_state: BOOL; // Error state flag
last_error: STRING; // Last error message
END_STRUCT
END_TYPE
VAR_GLOBAL
water_boiler: struct_water_boiler;
END_VAR
```
### Water Boiler Function Block
```iec
FUNCTION_BLOCK fb_waterBoiler
VAR_INPUT
// Control
ha_on: BOOL;
ha_off: BOOL;
schedule_on: BOOL;
schedule_off: BOOL;
// Sensors
temperature: REAL; // Water temperature sensor
pressure: REAL; // Water pressure (if available)
// Safety Inputs
emergency_stop: BOOL; // Emergency stop button
flow_sensor: BOOL; // Water flow sensor
overtemp_sensor: BOOL; // Over-temperature sensor
pressure_sensor: BOOL; // Pressure sensor (if available)
// Relay Output
relay_output: BOOL; // Read actual relay state
END_VAR
VAR_OUTPUT
relay_control: BOOL; // Control output to relay
state: BOOL; // Current state
relay_status: BOOL; // Actual relay status
safety_override: BOOL; // Safety override active
error_state: BOOL; // Error detected
error_code: INT; // Error code
error_message: STRING; // Error description
END_VAR
VAR
// Internal State
internal_state: BOOL;
last_state: BOOL;
// Safety Timers
max_on_time: TIME := T#8H; // Maximum ON time (8 hours)
min_off_time: TIME := T#30M; // Minimum OFF time (30 minutes)
on_timer: TON;
off_timer: TON;
// Temperature Limits
max_temp: REAL := 80.0; // Maximum temperature (°C)
min_temp: REAL := 10.0; // Minimum temperature (°C)
// Safety Flags
temp_ok: BOOL;
pressure_ok: BOOL;
flow_ok: BOOL;
time_ok: BOOL;
// Edge Detection
r_trig_ha_on: R_TRIG;
r_trig_ha_off: R_TRIG;
r_trig_schedule_on: R_TRIG;
r_trig_schedule_off: R_TRIG;
r_trig_emergency: R_TRIG;
END_VAR
```
### Safety Logic Implementation
```iec
// Edge detection
r_trig_ha_on(CLK := ha_on);
r_trig_ha_off(CLK := ha_off);
r_trig_schedule_on(CLK := schedule_on);
r_trig_schedule_off(CLK := schedule_off);
r_trig_emergency(CLK := emergency_stop);
// Safety Checks
temp_ok := (temperature >= min_temp) AND (temperature <= max_temp);
pressure_ok := pressure_sensor; // Assuming normally closed safety switch
flow_ok := flow_sensor; // Water flow detected
time_ok := NOT on_timer.Q; // Not exceeded max ON time
// Safety Override (highest priority)
IF r_trig_emergency.Q OR NOT temp_ok OR NOT pressure_ok THEN
safety_override := TRUE;
internal_state := FALSE;
error_state := TRUE;
error_code := 1; // Safety override
error_message := 'Safety override active';
ELSIF NOT flow_ok AND internal_state THEN
// No water flow but boiler is on - turn off after delay
off_timer(IN := TRUE, PT := T#5M); // 5 minute delay
IF off_timer.Q THEN
internal_state := FALSE;
error_state := TRUE;
error_code := 2; // No flow
error_message := 'No water flow detected';
END_IF;
ELSIF NOT time_ok THEN
// Maximum ON time exceeded
internal_state := FALSE;
error_state := TRUE;
error_code := 3; // Time exceeded
error_message := 'Maximum ON time exceeded';
ELSE
safety_override := FALSE;
error_state := FALSE;
error_code := 0;
error_message := '';
END_IF;
// Control Logic (only if safety OK)
IF NOT safety_override THEN
// Priority: Emergency OFF > Manual OFF > Scheduled OFF > Manual ON > Scheduled ON
IF r_trig_ha_off.Q OR r_trig_schedule_off.Q THEN
internal_state := FALSE;
ELSIF r_trig_ha_on.Q OR r_trig_schedule_on.Q THEN
IF temp_ok AND flow_ok AND time_ok THEN
internal_state := TRUE;
on_timer(IN := internal_state, PT := max_on_time);
END_IF;
END_IF;
END_IF;
// Update timers
on_timer(IN := internal_state AND NOT safety_override, PT := max_on_time);
off_timer(IN := NOT internal_state, PT := min_off_time);
// Output Assignment
relay_control := internal_state AND NOT safety_override;
state := internal_state;
relay_status := relay_output; // Read from actual relay
```
### Safety Measures
#### 1. Temperature Protection
- **Max Temperature**: 80°C (configurable)
- **Min Temperature**: 10°C (freeze protection)
- **Action**: Auto-shutoff if temperature exceeds limits
#### 2. Time-Based Protection
- **Max ON Time**: 8 hours (prevents continuous operation)
- **Min OFF Time**: 30 minutes (prevents rapid cycling)
- **Action**: Force OFF after max time, prevent ON during min OFF time
#### 3. Flow Detection
- **Flow Sensor**: Monitors water flow
- **Action**: Shut off if no flow detected for 5 minutes (dry-run protection)
#### 4. Emergency Stop
- **Emergency Button**: Physical emergency stop
- **Action**: Immediate shutdown, requires manual reset
#### 5. Pressure Protection
- **Pressure Sensor**: Monitors system pressure
- **Action**: Shut off if pressure abnormal
#### 6. State Monitoring
- **Relay Status**: Read actual relay state
- **Action**: Detect relay failures, report status
#### 7. Error Reporting
- **Error Codes**:
- 0: No error
- 1: Safety override
- 2: No flow
- 3: Time exceeded
- 4: Temperature fault
- 5: Pressure fault
- **Error Messages**: Human-readable descriptions
### Water Boiler Network Variable
```iec
// Sent to Node-RED (Status)
VAR_GLOBAL
boiler_status: struct_water_boiler;
END_VAR
// Received from Node-RED (Commands)
VAR_GLOBAL
boiler_commands: STRUCT
ha_on: BOOL;
ha_off: BOOL;
schedule_on: BOOL;
schedule_off: BOOL;
END_STRUCT;
END_VAR
```
### Home Assistant Integration
**Entity Type**: `switch` or `water_heater`
```yaml
switch:
- platform: mqtt
name: "Water Boiler"
unique_id: "water_boiler"
state_topic: "homeassistant/water_boiler/state"
command_topic: "homeassistant/water_boiler/set"
state_value_template: "{{ value_json.state }}"
command_on_template: '{"ha_on":true}'
command_off_template: '{"ha_off":true}'
qos: 1
retain: true
sensor:
- platform: mqtt
name: "Water Boiler Temperature"
state_topic: "homeassistant/water_boiler/state"
value_template: "{{ value_json.temperature }}"
unit_of_measurement: "°C"
- platform: mqtt
name: "Water Boiler Error"
state_topic: "homeassistant/water_boiler/state"
value_template: "{{ value_json.error_message }}"
```
---
## Summary
### Light Configuration
- **Total Rooms**: 16 (15 indoor + outdoor areas)
- **Total Lights**: ~60-70 lights (varies by room)
- **Structure**: Hybrid (room-organized in CODESYS, flat in HA)
- **Naming**: `{room}_{function}` format
### Water Boiler
- **Control**: ON/OFF with scheduling
- **Safety**: 7 safety measures implemented
- **Monitoring**: Temperature, flow, pressure, time
- **Error Reporting**: Comprehensive error codes and messages
### Next Steps
1. Review and modify light names per room based on actual installation
2. Map lights to relay outputs
3. Implement new structure in CODESYS
4. Update Node-RED flows
5. Configure Home Assistant entities
6. Test all safety measures for water boiler
---
**Document Status**: Template - Modify with actual light configuration
**Last Updated**: January 27, 2026

View File

@@ -1,239 +0,0 @@
# Redesign Implementation Roadmap
## Overview
This document provides a step-by-step roadmap for implementing the redesigned structure based on the light naming configuration.
## Phase 1: Configuration & Planning
### Step 1.1: Review Light Naming Configuration
- [ ] Review `light-naming-configuration.md`
- [ ] Modify light names per room based on actual installation
- [ ] Remove unused lights from structure
- [ ] Add any missing lights
- [ ] Document relay-to-light mapping
### Step 1.2: Map Relays to Lights
- [ ] Create mapping table: Relay # → Room → Light Name
- [ ] Document in `relay-mapping.md`
- [ ] Verify all relays are accounted for
**Example Mapping**:
```
Relay 1 → kitchen → main
Relay 2 → kitchen → under_cabinet
Relay 3 → kitchen → island
Relay 4 → master_bedroom → main
Relay 5 → master_bedroom → bedside_left
...
```
### Step 1.3: Water Boiler Configuration
- [ ] Review water boiler safety requirements
- [ ] Configure temperature limits
- [ ] Configure time limits (max ON, min OFF)
- [ ] Identify sensor inputs (temperature, flow, pressure)
- [ ] Map boiler relay output
---
## Phase 2: CODESYS Structure Implementation
### Step 2.1: Create New Data Structures
- [ ] Create `struct_light_control` type
- [ ] Create `struct_water_boiler` type
- [ ] Create room-specific structures
- [ ] Create global `rooms` structure
### Step 2.2: Create Function Blocks
- [ ] Create `fb_lightControl` function block
- [ ] Create `fb_waterBoiler` function block
- [ ] Test function blocks in simulation
### Step 2.3: Update Network Variables
- [ ] Update `NVL_Sender` with new structure
- [ ] Update `NVL_Receiver` with new structure
- [ ] Add water boiler network variables
- [ ] Configure network variable properties
### Step 2.4: Implement Control Logic
- [ ] Replace `fb_switch` with new structure
- [ ] Implement light control per room
- [ ] Implement water boiler control
- [ ] Add relay output mapping
- [ ] Add relay status readback
### Step 2.5: Testing
- [ ] Test in simulation
- [ ] Test individual light control
- [ ] Test water boiler control
- [ ] Test safety measures
- [ ] Verify network variable transmission
---
## Phase 3: Node-RED Integration
### Step 3.1: Update Network Variable Nodes
- [ ] Update CODESYS network variable nodes
- [ ] Configure new variable structure
- [ ] Test bidirectional communication
### Step 3.2: Create Transformation Functions
- [ ] Create function to flatten room structure for HA
- [ ] Create function to convert HA commands to CODESYS format
- [ ] Create function for Zigbee edge detection
- [ ] Test transformations
### Step 3.3: Update MQTT Flows
- [ ] Update MQTT topics to new naming convention
- [ ] Configure Home Assistant auto-discovery
- [ ] Update Zigbee switch flows
- [ ] Add water boiler MQTT flows
### Step 3.4: Testing
- [ ] Test HA → CODESYS commands
- [ ] Test Zigbee → CODESYS commands
- [ ] Test CODESYS → HA status updates
- [ ] Test water boiler control
- [ ] Verify MQTT message flow
---
## Phase 4: Home Assistant Configuration
### Step 4.1: Entity Configuration
- [ ] Create light entities for all lights
- [ ] Configure entity names and icons
- [ ] Set up areas/rooms in HA
- [ ] Create water boiler entity
### Step 4.2: Dashboard Setup
- [ ] Create lighting dashboard
- [ ] Group lights by room
- [ ] Add water boiler control
- [ ] Add status indicators
### Step 4.3: Automations
- [ ] Update existing automations
- [ ] Create new automations if needed
- [ ] Test automations
---
## Phase 5: Migration & Deployment
### Step 5.1: Backup Current System
- [ ] Backup CODESYS project
- [ ] Export Node-RED flows
- [ ] Backup Home Assistant configuration
- [ ] Document current state
### Step 5.2: Parallel Testing
- [ ] Deploy new structure in test mode
- [ ] Run parallel with old system
- [ ] Compare behavior
- [ ] Fix any issues
### Step 5.3: Gradual Migration
- [ ] Migrate one room at a time
- [ ] Test each room thoroughly
- [ ] Migrate water boiler
- [ ] Complete migration
### Step 5.4: Validation
- [ ] Test all lights from HA
- [ ] Test all lights from Zigbee switches
- [ ] Verify status feedback
- [ ] Test water boiler control
- [ ] Test all safety measures
- [ ] Verify no regressions
---
## Implementation Checklist
### CODESYS
- [ ] New data structures defined
- [ ] Function blocks created and tested
- [ ] Network variables updated
- [ ] Control logic implemented
- [ ] Relay mapping configured
- [ ] Status readback implemented
- [ ] Water boiler safety measures implemented
- [ ] Code tested in simulation
### Node-RED
- [ ] Network variable nodes updated
- [ ] Transformation functions created
- [ ] MQTT flows updated
- [ ] HA auto-discovery configured
- [ ] Zigbee integration updated
- [ ] Water boiler flows added
- [ ] All flows tested
### Home Assistant
- [ ] All light entities created
- [ ] Water boiler entity created
- [ ] Areas/rooms configured
- [ ] Dashboard updated
- [ ] Automations updated
- [ ] All entities tested
### Documentation
- [ ] Light naming configuration finalized
- [ ] Relay mapping documented
- [ ] Network variable structure documented
- [ ] MQTT topics documented
- [ ] Safety measures documented
- [ ] Troubleshooting guide updated
---
## Key Files Reference
1. **Light Configuration**: `light-naming-configuration.md`
2. **Structure Analysis**: `structure-redesign-analysis.md`
3. **Visual Comparison**: `structure-comparison-visual.md`
4. **Function Block Redesign**: `fb_switch-redesign-recommendation.md`
5. **This Roadmap**: `redesign-implementation-roadmap.md`
---
## Estimated Timeline
- **Phase 1** (Planning): 1-2 days
- **Phase 2** (CODESYS): 2-3 days
- **Phase 3** (Node-RED): 1-2 days
- **Phase 4** (Home Assistant): 1 day
- **Phase 5** (Migration): 2-3 days
**Total**: ~7-11 days (depending on complexity and testing)
---
## Risk Mitigation
### Risks
1. **Network Variable Changes**: May break existing Node-RED flows
- **Mitigation**: Test thoroughly, have rollback plan
2. **State Synchronization**: During migration, states may be inconsistent
- **Mitigation**: Migrate during low-usage period, verify all states
3. **Water Boiler Safety**: Critical system, must work correctly
- **Mitigation**: Extensive testing, manual override available
4. **Home Assistant Entities**: May need to recreate entities
- **Mitigation**: Use unique_id to preserve entity history
### Rollback Plan
- Keep old CODESYS project as backup
- Keep old Node-RED flows as backup
- Document rollback procedure
- Test rollback procedure
---
**Status**: Planning Phase
**Next Step**: Review and modify `light-naming-configuration.md` with actual light configuration

View File

@@ -1,101 +0,0 @@
# Redesign Summary - Quick Reference
## What We're Changing
### Current Structure → New Structure
**Before**:
- Room-based with generic `l_1`, `l_2` names
- Fixed 6 lights per room
- Toggle-based control
- No status feedback from relays
**After**:
- Room-based in CODESYS, flat in Home Assistant
- Descriptive names: `kitchen_main`, `master_bedroom_bedside_left`
- Command-based control (ON/OFF for HA, toggle for Zigbee)
- Actual relay status feedback
- Water boiler with comprehensive safety
---
## Key Documents
1. **`light-naming-configuration.md`** ⭐ **START HERE**
- Complete light naming for all 16 rooms
- Water boiler control structure
- Safety measures
- **Action**: Review and modify with your actual lights
2. **`redesign-implementation-roadmap.md`**
- Step-by-step implementation guide
- Phase-by-phase checklist
- Timeline estimates
3. **`structure-redesign-analysis.md`**
- Detailed analysis of structure options
- Why Option 4 (Hybrid) was chosen
4. **`fb_switch-redesign-recommendation.md`**
- Function block redesign details
- Control logic improvements
---
## Quick Structure Reference
### Light Naming Convention
```
Format: {room}_{function}
Examples:
- kitchen_main
- master_bedroom_bedside_left
- outdoor_front_security
```
### Function Types
- `main` - Primary light
- `bedside_left/right` - Bedside lamps
- `under_cabinet` - Task lighting
- `security` - Security lighting
- `ambient` - Atmospheric lighting
- etc.
### CODESYS Access
```iec
rooms.kitchen.main.ha_on := TRUE;
rooms.master_bedroom.bedside_left.state;
```
### Home Assistant Entity
```
light.kitchen_main
light.master_bedroom_bedside_left
```
---
## Water Boiler Safety Features
1.**Temperature Protection** (max 80°C, min 10°C)
2.**Time Limits** (max 8h ON, min 30m OFF)
3.**Flow Detection** (shut off if no flow)
4.**Emergency Stop** (immediate shutdown)
5.**Pressure Protection** (if sensor available)
6.**State Monitoring** (relay status readback)
7.**Error Reporting** (comprehensive error codes)
---
## Next Steps
1. **Review** `light-naming-configuration.md`
2. **Modify** light names per room based on actual installation
3. **Map** relays to lights
4. **Start** Phase 1 of implementation roadmap
---
**Status**: Ready for configuration review
**Priority**: Review and customize light names first

View File

@@ -1,362 +0,0 @@
# Structure Comparison - Visual Guide
## Current Structure (Room-Centric)
```
CODESYS:
┌─────────────────────────────────────┐
│ masterBedroom: fb_switch │
│ ├─ l_1 (Light 1) │
│ ├─ l_2 (Light 2) │
│ ├─ l_3 (Light 3) │
│ ├─ l_4 (Light 4) │
│ ├─ l_5 (Light 5) │
│ └─ l_6 (Light 6) │
└─────────────────────────────────────┘
Network Variable:
l_masterBedroom.l_1
l_masterBedroom.l_2
...
Home Assistant:
light.master_bedroom_l1
light.master_bedroom_l2
light.master_bedroom_l3
...
```
**Problems**:
- ❌ Generic names (l_1, l_2)
- ❌ Fixed 6 lights per room
- ❌ Wasted space (most rooms don't use all 6)
- ❌ Hard to identify which light is which
---
## Option 1: Flat Light-Centric
```
CODESYS:
┌─────────────────────────────────────┐
│ lights: struct_all_lights │
│ ├─ kitchen_main │
│ ├─ kitchen_under_cabinet │
│ ├─ master_bedroom_main │
│ ├─ master_bedroom_bedside_left │
│ ├─ master_bedroom_bedside_right │
│ └─ ... (all lights flat) │
└─────────────────────────────────────┘
Network Variable:
lights.kitchen_main.on
lights.kitchen_main.state
lights.master_bedroom_main.on
...
Home Assistant:
light.kitchen_main
light.kitchen_under_cabinet
light.master_bedroom_main
light.master_bedroom_bedside_left
```
**Benefits**:
- ✅ Clear, descriptive names
- ✅ Easy to find lights
- ✅ No wasted space
- ✅ Perfect for HA organization
---
## Option 4: Hybrid (Recommended)
```
CODESYS (Room-organized):
┌─────────────────────────────────────┐
│ rooms: struct_rooms │
│ ├─ kitchen │
│ │ ├─ main │
│ │ ├─ under_cabinet │
│ │ └─ island │
│ ├─ master_bedroom │
│ │ ├─ main │
│ │ ├─ bedside_left │
│ │ └─ bedside_right │
│ └─ ... │
└─────────────────────────────────────┘
Network Variable:
rooms.kitchen.main.on
rooms.kitchen.main.state
rooms.master_bedroom.main.on
...
Node-RED (Flattened):
kitchen_main → HA
kitchen_under_cabinet → HA
master_bedroom_main → HA
master_bedroom_bedside_left → HA
Home Assistant:
light.kitchen_main
light.kitchen_under_cabinet
light.master_bedroom_main
light.master_bedroom_bedside_left
```
**Benefits**:
- ✅ Organized in CODESYS (by room)
- ✅ Flat in Home Assistant (by light)
- ✅ Descriptive names
- ✅ Easy to add lights
- ✅ Best of both worlds
---
## Real-World Example Comparison
### Scenario: Kitchen with 3 lights
**Current Structure**:
```iec
kitchen: fb_switch
l_1 = ??? (which light?)
l_2 = ??? (which light?)
l_3 = ??? (which light?)
l_4 = unused
l_5 = unused
l_6 = unused
```
**Hybrid Structure**:
```iec
rooms.kitchen:
main = Main ceiling light
under_cabinet = Under cabinet lights
island = Island pendant
(no unused slots)
```
**Home Assistant**:
```
Current:
light.kitchen_l1 ← What is this?
light.kitchen_l2 ← What is this?
light.kitchen_l3 ← What is this?
Hybrid:
light.kitchen_main ← Clear!
light.kitchen_under_cabinet ← Clear!
light.kitchen_island ← Clear!
```
---
## MQTT Topic Comparison
### Current
```
homeassistant/light/master_bedroom_l1/state
homeassistant/light/master_bedroom_l2/state
homeassistant/light/kitchen_l1/state
```
**Problems**: Generic, hard to identify
### Hybrid
```
homeassistant/light/kitchen_main/state
homeassistant/light/kitchen_under_cabinet/state
homeassistant/light/master_bedroom_main/state
homeassistant/light/master_bedroom_bedside_left/state
```
**Benefits**: Self-documenting, clear purpose
---
## Home Assistant Dashboard Comparison
### Current
```
Kitchen
├─ Light 1 ← What is this?
├─ Light 2 ← What is this?
└─ Light 3 ← What is this?
Master Bedroom
├─ Light 1 ← What is this?
├─ Light 2 ← What is this?
└─ Light 3 ← What is this?
```
### Hybrid
```
Kitchen
├─ Main Light
├─ Under Cabinet
└─ Island
Master Bedroom
├─ Main Light
├─ Bedside Left
└─ Bedside Right
```
**Much clearer!**
---
## Network Variable Efficiency
### Current
- 15 rooms × 6 lights = 90 potential lights
- Most rooms use 2-3 lights
- **Waste**: ~60 unused light slots
- **Network traffic**: Sends all 90 values every 50ms
### Hybrid
- Only define lights that exist
- Kitchen: 3 lights (not 6)
- Master Bedroom: 3 lights (not 6)
- **Efficiency**: ~50% less network traffic
- **Clarity**: Only relevant data
---
## Scalability Comparison
### Adding a New Light
**Current**:
1. Find unused `l_X` slot in room
2. Hope it's available
3. Update documentation
4. Update HA manually
**Hybrid**:
1. Add `new_light: struct_light_control` to room
2. Done! Auto-discovered in HA
### Adding a New Room
**Current**:
1. Add new room structure
2. Add all 6 light slots (even if unused)
3. Update network variables
4. Update HA manually
**Hybrid**:
1. Add room with only needed lights
2. Done! Auto-discovered in HA
---
## Implementation Example
### Hybrid Structure in CODESYS
```iec
TYPE struct_light_control :
STRUCT
on: BOOL;
off: BOOL;
toggle: BOOL;
state: BOOL;
relay_status: BOOL;
END_STRUCT
END_TYPE
TYPE struct_room_lights :
STRUCT
main: struct_light_control;
secondary: struct_light_control;
task: struct_light_control;
ambient: struct_light_control;
accent: struct_light_control;
security: struct_light_control;
END_STRUCT
END_TYPE
VAR_GLOBAL
rooms: STRUCT
kitchen: STRUCT
main: struct_light_control;
under_cabinet: struct_light_control;
island: struct_light_control;
END_STRUCT;
master_bedroom: STRUCT
main: struct_light_control;
bedside_left: struct_light_control;
bedside_right: struct_light_control;
END_STRUCT;
// ... other rooms
END_STRUCT;
END_VAR
```
### Node-RED Transformation
```javascript
// Transform room-based to flat structure for HA
function transformToHA(msg) {
const rooms = msg.payload.rooms;
const result = {};
for (const [roomName, roomLights] of Object.entries(rooms)) {
for (const [lightName, lightControl] of Object.entries(roomLights)) {
const lightId = `${roomName}_${lightName}`;
result[lightId] = {
on: lightControl.on,
off: lightControl.off,
state: lightControl.state,
status: lightControl.relay_status
};
}
}
return { payload: result };
}
```
### Home Assistant Auto-Discovery
```yaml
# Node-RED publishes this automatically
homeassistant/light/kitchen_main/config:
name: "Kitchen Main"
unique_id: "kitchen_main"
state_topic: "homeassistant/light/kitchen_main/state"
command_topic: "homeassistant/light/kitchen_main/set"
state_value_template: "{{ value_json.state }}"
command_on_template: '{"on":true}'
command_off_template: '{"off":true}'
```
---
## Recommendation Summary
**Use Hybrid Structure (Option 4)** because:
1.**Best Organization**: Room-based in CODESYS, flat in HA
2.**Clear Naming**: `kitchen_main` vs `l_1`
3.**Efficiency**: Only define existing lights
4.**Scalability**: Easy to add lights/rooms
5.**Maintainability**: Self-documenting structure
6.**HA Integration**: Perfect for Home Assistant
7.**Future-Proof**: Easy to extend
**Key Changes**:
- Replace `l_1`, `l_2` with descriptive names
- Use function-based naming: `main`, `secondary`, `task`, etc.
- Flatten structure in Node-RED for HA
- Only define lights that exist
- Add metadata for better organization
---
**See**: [Full Structure Redesign Analysis](structure-redesign-analysis.md) for complete details.

View File

@@ -1,538 +0,0 @@
# Room/Light Structure Redesign Analysis
## Current Structure Analysis
### Current Organization
**Room-Centric Approach**:
- 15 rooms, each with 1 `fb_switch` instance
- Each room controls up to 6 lights
- Network variables organized by room name
- Structure: `l_masterBedroom.l_1`, `l_masterBedroom.l_2`, etc.
**Current Data Flow**:
```
Room → fb_switch → 6 lights
Network Variable: l_masterBedroom (struct_lights with l_1 to l_6)
```
### Current Issues
1. **Limited Scalability**
- Fixed 6 lights per room
- Adding a 7th light requires structure changes
- Some rooms may only need 1-2 lights (waste)
2. **Home Assistant Organization**
- HA entities: `light.master_bedroom_l1`, `light.master_bedroom_l2`
- Not intuitive - hard to find lights
- No grouping by function or zone
3. **Network Variable Efficiency**
- 15 room structures × 6 lights = 90 potential lights
- Most rooms don't use all 6 lights
- Wasted network bandwidth
4. **Maintenance Complexity**
- Changes require updating room structures
- Hard to add new rooms
- No clear naming convention
5. **MQTT Topic Structure**
- Topics like: `homeassistant/light/master_bedroom_l1`
- Not standardized
- Hard to discover and manage
## Alternative Structures
### Option 1: Flat Light-Centric Structure (Recommended)
**Concept**: All lights in a single flat structure, organized by unique IDs.
**Data Structure**:
```iec
TYPE struct_light :
STRUCT
// Control
on: BOOL; // Turn ON command
off: BOOL; // Turn OFF command
toggle: BOOL; // Toggle command (for Zigbee)
// Status
state: BOOL; // Current state
relay_status: BOOL; // Actual relay status
// Metadata (for reference, not in network var)
room: STRING; // Room name
name: STRING; // Light name
zone: STRING; // Zone (indoor/outdoor)
END_STRUCT
END_TYPE
TYPE struct_all_lights :
STRUCT
// Master Bedroom
master_bedroom_main: struct_light;
master_bedroom_bedside: struct_light;
master_bedroom_closet: struct_light;
// Kitchen
kitchen_ceiling: struct_light;
kitchen_under_cabinet: struct_light;
kitchen_island: struct_light;
// Living Room
living_room_main: struct_light;
living_room_reading: struct_light;
living_room_tv: struct_light;
// Outdoor
outdoor_front_porch: struct_light;
outdoor_back_patio: struct_light;
outdoor_side: struct_light;
// ... all other lights
END_STRUCT
END_TYPE
```
**Network Variable**:
```iec
VAR_GLOBAL
lights: struct_all_lights; // All lights in one structure
END_VAR
```
**Function Block**:
```iec
FUNCTION_BLOCK fb_lightControl
VAR_INPUT
light: struct_light;
relay_output: BOOL; // Direct relay output
END_VAR
VAR_OUTPUT
relay_control: BOOL;
light_status: struct_light;
END_VAR
// Implementation...
END_FUNCTION_BLOCK
```
**Pros**:
- ✅ Each light is independent
- ✅ Easy to add/remove lights
- ✅ Clear naming: `lights.kitchen_ceiling.on`
- ✅ Better for Home Assistant (one entity per light)
- ✅ No wasted space
- ✅ Easy to group in HA by room/zone
**Cons**:
- ⚠️ Larger structure (but more efficient overall)
- ⚠️ Need to update structure when adding lights
**Home Assistant Organization**:
```
light.kitchen_ceiling
light.kitchen_under_cabinet
light.master_bedroom_main
light.master_bedroom_bedside
```
**MQTT Topics**:
```
homeassistant/light/kitchen_ceiling/state
homeassistant/light/kitchen_ceiling/set
homeassistant/light/master_bedroom_main/state
```
---
### Option 2: Hierarchical Room → Light Structure
**Concept**: Keep room structure but make it more flexible.
**Data Structure**:
```iec
TYPE struct_room :
STRUCT
room_id: STRING; // "master_bedroom"
room_name: STRING; // "Master Bedroom"
zone: STRING; // "indoor", "outdoor"
// Flexible light array (up to 10 lights)
lights: ARRAY[1..10] OF struct_light;
light_count: INT; // Actual number of lights
END_STRUCT
END_TYPE
TYPE struct_home :
STRUCT
rooms: ARRAY[1..20] OF struct_room;
room_count: INT;
END_STRUCT
END_TYPE
```
**Pros**:
- ✅ Maintains room grouping
- ✅ Flexible number of lights per room
- ✅ Can add rooms easily
**Cons**:
- ⚠️ More complex array handling
- ⚠️ CODESYS arrays less efficient for network vars
- ⚠️ Harder to access specific lights
---
### Option 3: Zone-Based Structure
**Concept**: Organize by functional zones rather than rooms.
**Data Structure**:
```iec
TYPE struct_zone :
STRUCT
zone_id: STRING; // "indoor_main", "outdoor_security"
zone_name: STRING; // "Indoor Main", "Outdoor Security"
// Lights in this zone
lights: ARRAY[1..20] OF struct_light;
light_count: INT;
END_STRUCT
END_TYPE
TYPE struct_home_zones :
STRUCT
indoor_main: struct_zone;
indoor_bedrooms: struct_zone;
indoor_bathrooms: struct_zone;
outdoor_security: struct_zone;
outdoor_ambient: struct_zone;
END_STRUCT
END_TYPE
```
**Zones**:
- **Indoor Main**: Kitchen, Living Room, Dining Room, Hallway
- **Indoor Bedrooms**: All bedrooms
- **Indoor Bathrooms**: All bathrooms
- **Outdoor Security**: Front, Back, Side (security-focused)
- **Outdoor Ambient**: Veranda, Patio (ambient lighting)
**Pros**:
- ✅ Logical grouping for automation
- ✅ Easy to control all security lights
- ✅ Better for scheduling (indoor vs outdoor)
**Cons**:
- ⚠️ Less intuitive for room-based control
- ⚠️ Lights may belong to multiple zones conceptually
---
### Option 4: Hybrid: Room + Light ID Structure (Best Balance)
**Concept**: Combine room organization with individual light control.
**Data Structure**:
```iec
TYPE struct_light_control :
STRUCT
// Control
on: BOOL;
off: BOOL;
toggle: BOOL;
// Status
state: BOOL;
relay_status: BOOL;
// Metadata (for HA, not in network var)
light_id: STRING; // Unique ID: "mb_main", "kit_ceiling"
light_name: STRING; // Display name: "Main Light", "Ceiling Light"
room: STRING; // Room: "master_bedroom", "kitchen"
zone: STRING; // Zone: "indoor", "outdoor"
function: STRING; // Function: "main", "task", "ambient", "security"
END_STRUCT
END_TYPE
// Organized by room for CODESYS, but flat for HA
TYPE struct_rooms :
STRUCT
// Master Bedroom
master_bedroom: STRUCT
main: struct_light_control;
bedside_left: struct_light_control;
bedside_right: struct_light_control;
closet: struct_light_control;
END_STRUCT;
// Kitchen
kitchen: STRUCT
ceiling: struct_light_control;
under_cabinet: struct_light_control;
island: struct_light_control;
END_STRUCT;
// ... other rooms
END_STRUCT
END_TYPE
```
**Network Variable**:
```iec
VAR_GLOBAL
rooms: struct_rooms; // Organized by room
END_VAR
```
**Access Pattern**:
- CODESYS: `rooms.kitchen.ceiling.on`
- Node-RED: Flatten to `kitchen_ceiling` for HA
- Home Assistant: `light.kitchen_ceiling`
**Pros**:
- ✅ Best of both worlds
- ✅ Organized in CODESYS (by room)
- ✅ Flat in Home Assistant (by light)
- ✅ Easy to add lights to rooms
- ✅ Clear naming convention
**Cons**:
- ⚠️ Requires Node-RED transformation
- ⚠️ Slightly more complex
---
## Recommended Structure: Option 4 (Hybrid)
### Implementation
**1. CODESYS Structure** (Room-organized):
```iec
TYPE struct_light_control :
STRUCT
on: BOOL;
off: BOOL;
toggle: BOOL;
state: BOOL;
relay_status: BOOL;
END_STRUCT
END_TYPE
TYPE struct_room_lights :
STRUCT
main: struct_light_control;
secondary: struct_light_control;
task: struct_light_control;
ambient: struct_light_control;
accent: struct_light_control;
security: struct_light_control;
END_STRUCT
END_TYPE
VAR_GLOBAL
rooms: STRUCT
master_bedroom: struct_room_lights;
master_bathroom: struct_room_lights;
bedroom_1: struct_room_lights;
bedroom_2: struct_room_lights;
bathroom: struct_room_lights;
kitchen: struct_room_lights;
living_room: struct_room_lights;
dining_room: struct_room_lights;
hallway: struct_room_lights;
pantry: struct_room_lights;
entrance: struct_room_lights;
guest_wc: struct_room_lights;
outdoor_veranda: struct_room_lights;
outdoor_front: struct_room_lights;
outdoor_side: struct_room_lights;
outdoor_back: struct_room_lights;
END_STRUCT;
END_VAR
```
**2. Node-RED Transformation** (Flatten for HA):
```javascript
// Transform CODESYS structure to flat HA structure
function transformToHA(msg) {
const rooms = msg.payload.rooms;
const haLights = {};
// Flatten structure
for (const [roomName, roomLights] of Object.entries(rooms)) {
for (const [lightName, lightControl] of Object.entries(roomLights)) {
const lightId = `${roomName}_${lightName}`;
haLights[lightId] = {
on: lightControl.on,
off: lightControl.off,
state: lightControl.state,
status: lightControl.relay_status,
room: roomName,
light: lightName
};
}
}
return { payload: haLights };
}
```
**3. Home Assistant Configuration**:
```yaml
# Auto-discovery via MQTT
light:
- platform: mqtt
name: "Kitchen Main"
unique_id: "kitchen_main"
state_topic: "homeassistant/light/kitchen_main/state"
command_topic: "homeassistant/light/kitchen_main/set"
# ... config
```
**4. MQTT Topic Structure**:
```
homeassistant/light/{room}_{light}/state
homeassistant/light/{room}_{light}/set
Examples:
homeassistant/light/kitchen_main/state
homeassistant/light/master_bedroom_main/state
homeassistant/light/outdoor_front_security/state
```
---
## Comparison Matrix
| Aspect | Current | Option 1 (Flat) | Option 2 (Hierarchical) | Option 3 (Zone) | Option 4 (Hybrid) |
|--------|---------|-----------------|-------------------------|------------------|-------------------|
| **CODESYS Organization** | Room-based | Flat | Array-based | Zone-based | Room-based |
| **HA Organization** | Room-based | Flat | Complex | Zone-based | Flat (transformed) |
| **Scalability** | Poor | Excellent | Good | Good | Excellent |
| **Maintainability** | Medium | Excellent | Medium | Medium | Excellent |
| **Network Efficiency** | Poor | Good | Medium | Medium | Good |
| **Naming Clarity** | Medium | Excellent | Medium | Good | Excellent |
| **Implementation Complexity** | Low | Medium | High | Medium | Medium |
| **Flexibility** | Low | High | High | Medium | High |
---
## Optimization Recommendations
### 1. Network Variable Optimization
**Current**: 15 rooms × 6 lights = 90 potential lights
**Optimized**: Only include lights that exist
**Example**:
```iec
// Only define lights that exist
master_bedroom: STRUCT
main: struct_light_control;
bedside: struct_light_control;
// No closet light if it doesn't exist
END_STRUCT;
```
### 2. Function-Based Naming
Instead of `l_1`, `l_2`, use descriptive names:
- `main` - Main/primary light
- `secondary` - Secondary light
- `task` - Task lighting
- `ambient` - Ambient/atmospheric
- `accent` - Accent lighting
- `security` - Security/outdoor
### 3. Metadata for Home Assistant
Add metadata structure (not in network var, but in Node-RED):
```javascript
const lightMetadata = {
"kitchen_main": {
name: "Kitchen Main Light",
room: "Kitchen",
zone: "indoor",
function: "main",
icon: "mdi:ceiling-light"
},
// ... more lights
};
```
### 4. Grouping in Home Assistant
Use HA groups and areas:
```yaml
# Automatically group by room
light:
- platform: group
name: "Kitchen Lights"
entities:
- light.kitchen_main
- light.kitchen_under_cabinet
- light.kitchen_island
# Use areas for room organization
areas:
- name: "Kitchen"
entities:
- light.kitchen_main
- light.kitchen_under_cabinet
```
---
## Migration Strategy
### Phase 1: Structure Redesign
1. Define new data structures
2. Create mapping from old to new
3. Update function blocks
### Phase 2: Node-RED Update
1. Update network variable mapping
2. Add transformation functions
3. Update MQTT topics
### Phase 3: Home Assistant Migration
1. Create new entities with new structure
2. Migrate automations
3. Update dashboards
### Phase 4: Testing & Validation
1. Test all control paths
2. Verify status feedback
3. Test HA integration
---
## Final Recommendation
**Use Option 4 (Hybrid Structure)** because:
1.**Best Organization**: Room-based in CODESYS, flat in HA
2.**Scalability**: Easy to add lights to any room
3.**Clarity**: Descriptive names (`kitchen_main` vs `l_1`)
4.**Flexibility**: Can group by room, zone, or function
5.**Efficiency**: Only define lights that exist
6.**Maintainability**: Clear structure, easy to understand
**Key Improvements**:
- Replace `l_1`, `l_2` with `main`, `secondary`, `task`, etc.
- Use descriptive light IDs: `kitchen_main`, `master_bedroom_bedside`
- Flatten structure in Node-RED for Home Assistant
- Add metadata for better HA organization
- Optimize network variables (only existing lights)
---
**Next Steps**:
1. Review this analysis
2. Choose structure option
3. Plan migration
4. Implement new structure
5. Update Node-RED and HA