#:########################################################################################:#
# TITLE: DOWNSTAIRS LOUNGE MAIN LIGHTS
# zorruno.com layout v1.1 2026
#:########################################################################################:#
# REPO:
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-downstloungemain.yaml
#:########################################################################################:#
# VERSIONS:
# v3.8 2026-07-25 Two-way MQTT sync with entry switch; scripts, template switches,
#                  on_connect republish, state guards, internal relays
# v3.7 2026-07-20 Simplified local MQTT command and status topics to the device root
# v3.6 2026-02-25 Updated yaml to zorruno layout V1.1
# v3.5 2025-07-28 YAML tidyups
#:########################################################################################:#
# HARDWARE:
# - Zemismart KS-811 Triple push button (ESP8266)
# - Pinout/Schematic: https://community.home-assistant.io/t/zemismart-ks-811-working-with-esphome/
#:########################################################################################:#
# OPERATION NOTES:
# - 3-gang KS-811 triple switch.
# - Button 1 toggles Relay 1 (Main Lights) via script that publishes MQTT command.
# - Button 2 toggles Relay 2 (Back Wall Lights).
# - Button 3 toggles Relay 3 (Spare). NOTE: nothing connected to this output.
# - Template switches expose HA control that also publishes MQTT commands.
# - Physical relays publish retained MQTT status on state changes.
# - State guards prevent ping-pong between local and remote control.
# - GPIO16 does not support interrupts; polling is used for Button 1 (suppresses warning).
#:########################################################################################:#
# MQTT COMMANDS:
# - Local command topic (Main Lights):
#   ${mqtt_local_command_topic}
#     Payload "On"  -> Relay 1 ON
#     Payload "Off" -> Relay 1 OFF
#
# - Local status topic (Main Lights):
#   ${mqtt_local_status_topic}
#     Payload "On" / "Off" published retained when Relay 1 changes state
#
# - Button presses and HA control publish command topics.
# - Relay callbacks publish retained status only (no command ping-pong).
#:########################################################################################:#
# OFFLINE NOTES:
# a) Home Assistant OFFLINE, WiFi OK
# - Physical buttons still control relays locally.
# - MQTT local control still works if broker is reachable.
#
# b) MQTT OFFLINE (or broker unreachable)
# - Physical buttons still control relays locally.
# - HA/API control (if available) still works, but no MQTT command/status.
#
# c) Entire WiFi/Network OFFLINE
# - Physical buttons still control relays locally.
# - Accurate time is NOT needed (SNTP not needed).
#:########################################################################################:#

#:########################################################################################:#
# SUBSTITUTIONS: Specific device variable substitutions
# If NOT using a secrets file, just replace these with the passwords etc (in quotes)
#:########################################################################################:#
substitutions:
  # Device Naming
  device_name: "esp-downstloungemain"
  friendly_name: "Downstairs Lounge Main Lightswitch (3)"
  description_comment: "Downstairs Bedroom Main Lightswitch using a Zemismart KS-811 Triple Push Button. Main Lights (1), Back Wall Lights (2), Spare (3) (Layout V1.1)"
  device_area: "Downstairs Flat"

  # Project Naming
  project_name: "Zemismart Technologies.KS-811 Triple"
  project_version: "v3.8"

  # Passwords & Secrets
  api_key: !secret esp-api_key
  ota_pass: !secret esp-ota_pass
  static_ip_address: !secret esp-downstloungemain_ip
  mqtt_command_main_topic: !secret mqtt_command_main_topic
  mqtt_status_main_topic: !secret mqtt_status_main_topic

  # If we are changing IP addresses, you must update the current IP address here, otherwise it remains
  # Don't forget to switch it back when changed.
  current_ip_address: ${static_ip_address}

  # Device Settings
  log_level: "INFO"
  update_interval: "60s"

  # Default relay restore mode
  relay_restore_mode: "RESTORE_DEFAULT_OFF"

  # MQTT LOCAL Controls
  mqtt_device_name: "downst-lounge-main-lights"
  mqtt_local_command_topic: "${mqtt_command_main_topic}/${mqtt_device_name}"
  mqtt_local_status_topic: "${mqtt_status_main_topic}/${mqtt_device_name}"

  # Switch/Relay/Button Naming
  switch_1_name: "Main Lights"
  switch_2_name: "Back Wall Lights"
  switch_3_name: "Spare" # NOTE, Nothing connected to this switch output

#:########################################################################################:#
# PACKAGES: Included Common Packages
# https://esphome.io/components/packages.html
#:########################################################################################:#
packages:
  #### WIFI, Network (Static/DHCP/IPV6 etc), Fallback AP, Safemode ####
  common_wifi: !include
    file: common/network_common.yaml
    vars:
      local_device_name: "${device_name}"
      local_static_ip_address: "${static_ip_address}"
      local_ota_pass: "${ota_pass}"
      local_current_ip_address: "${current_ip_address}"

  #### HOME ASSISTANT API (choose encryption or no encryption options) ####
  common_api: !include
    file: common/api_common.yaml
    #file: common/api_common_noencryption.yaml
    vars:
      local_api_key: "${api_key}"

  #### MQTT ####
  common_mqtt: !include
    file: common/mqtt_common.yaml
    vars:
      local_device_name: "${device_name}"

  #### DIAGNOSTICS Sensors ####
  diag_basic: !include common/include_basic_diag_sensors.yaml
  diag_more: !include common/include_more_diag_sensors.yaml
  #diag_debug: !include common/include_debug_diag_sensors.yaml
  #diag_resetcount: !include common/include_resetcount_diag_sensors.yaml

#:########################################################################################:#
# ESPHOME
# https://esphome.io/components/esphome.html
#:########################################################################################:#
esphome:
  name: "${device_name}"
  friendly_name: "${friendly_name}"
  comment: "${description_comment}"
  area: "${device_area}"
  project:
    name: "${project_name}"
    version: "${project_version}"

#:########################################################################################:#
# ESP PLATFORM AND FRAMEWORK
# https://esphome.io/components/esp8266.html
#:########################################################################################:#
esp8266:
  board: esp01_1m
  early_pin_init: false
  board_flash_mode: dout

#:########################################################################################:#
# LOGGING
# https://esphome.io/components/logger.html
#:########################################################################################:#
logger:
  level: "${log_level}"
  #baud_rate: 0

#:########################################################################################:#
# MQTT: Device-specific MQTT command triggers
# This adds device-specific MQTT command triggers to the common MQTT configuration.
#:########################################################################################:#
mqtt:
  # Republish current relay states whenever MQTT reconnects so retained status stays accurate
  # for subscribers that reconnect later.
  on_connect:
    - mqtt.publish:
        topic: "${mqtt_local_status_topic}"
        payload: !lambda |-
          return id(relay_1).state ? "On" : "Off";
        retain: true

  on_message:
    # Main Lights control (Relay 1) — state guard prevents ping-pong
    - topic: "${mqtt_local_command_topic}"
      payload: "On"
      then:
        - if:
            condition:
              lambda: "return !id(relay_1).state;"
            then:
              - switch.turn_on: relay_1
    - topic: "${mqtt_local_command_topic}"
      payload: "Off"
      then:
        - if:
            condition:
              lambda: "return id(relay_1).state;"
            then:
              - switch.turn_off: relay_1

#:########################################################################################:#
# STATUS LED
# https://esphome.io/components/status_led.html
#:########################################################################################:#
status_led:
  pin:
    number: GPIO2
    inverted: true

#:########################################################################################:#
# SCRIPTS
# https://esphome.io/components/script.html
#:########################################################################################:#
script:
  # Local/HA requested ON for Main Lights. Change the local relay immediately, then
  # publish a non-retained command so any linked switch follows.
  - id: mainlights_on
    mode: restart
    then:
      - if:
          condition:
            lambda: "return !id(relay_1).state;"
          then:
            - switch.turn_on: relay_1
      - mqtt.publish:
          topic: "${mqtt_local_command_topic}"
          payload: "On"
          retain: false

  # Local/HA requested OFF for Main Lights.
  - id: mainlights_off
    mode: restart
    then:
      - if:
          condition:
            lambda: "return id(relay_1).state;"
          then:
            - switch.turn_off: relay_1
      - mqtt.publish:
          topic: "${mqtt_local_command_topic}"
          payload: "Off"
          retain: false

  # Physical Button 1 toggle logic.
  - id: mainlights_toggle
    mode: restart
    then:
      - if:
          condition:
            lambda: "return id(relay_1).state;"
          then:
            - script.execute: mainlights_off
          else:
            - script.execute: mainlights_on

#:########################################################################################:#
# BINARY SENSORS
# https://esphome.io/components/binary_sensor/
#:########################################################################################:#
binary_sensor:
  # Button 1 (GPIO16) - polling (GPIO16 has no interrupts)
  - platform: gpio
    pin:
      number: GPIO16
      mode: INPUT
      inverted: true
    use_interrupt: false
    name: "Button 1: ${switch_1_name}"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
    on_press:
      - script.execute: mainlights_toggle

  # Button 2 (GPIO05)
  - platform: gpio
    pin:
      number: GPIO05
      mode: INPUT
      inverted: true
    name: "Button 2: ${switch_2_name}"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
    on_press:
      - switch.toggle: relay_2

  # Button 3 (GPIO04)
  - platform: gpio
    pin:
      number: GPIO04
      mode: INPUT
      inverted: true
    name: "Button 3: ${switch_3_name}"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
    on_press:
      - switch.toggle: relay_3

#:########################################################################################:#
# SWITCH
# https://esphome.io/components/switch/
#:########################################################################################:#
switch:
  # Exposed Home Assistant control for Main Lights. This controls the local relay and
  # publishes the matching command so linked switches follow.
  - platform: template
    name: "Relay 1: ${switch_1_name}"
    id: relay_1_ha
    lambda: |-
      return id(relay_1).state;
    turn_on_action:
      - script.execute: mainlights_on
    turn_off_action:
      - script.execute: mainlights_off

  # Physical Relay 1. Kept internal so HA uses the template switch above.
  # Relay callbacks publish retained status only, not commands.
  - platform: gpio
    name: "Internal Relay 1: ${switch_1_name}"
    internal: true
    pin: GPIO13
    id: relay_1
    restore_mode: ${relay_restore_mode}
    on_turn_on:
      - mqtt.publish:
          topic: "${mqtt_local_status_topic}"
          payload: "On"
          retain: true
    on_turn_off:
      - mqtt.publish:
          topic: "${mqtt_local_status_topic}"
          payload: "Off"
          retain: true

  # Relay 2 (GPIO12)
  - platform: gpio
    name: "Relay 2: ${switch_2_name}"
    pin: GPIO12
    id: relay_2

  # Relay 3 (GPIO14)
  - platform: gpio
    name: "Relay 3: ${switch_3_name}"
    pin: GPIO14
    id: relay_3
