# 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