#:########################################################################################:#
# TITLE: MAIN HALLWAY NIGHTLIGHTS SWITCH (BK7231N)
# zorruno.com layout v1.1 2026
#:########################################################################################:#
# REPO:
# https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-mainhallwaynightlights.yaml
#:########################################################################################:#
# VERSIONS:
# V1.2 2026-05-07 Refactored MQTT sync to use template HA switch actions; physical button and
#                 HA control now publish command topics; relay callbacks publish status only
#                 to prevent command ping-pong/chattering.
# V1.1 2026-04-03 Updated yaml to zorruno layout V1.1; corrected KS-811 single naming and
#                 operation notes; updated BK7231N single-switch pin mapping
# V1.0 2026-04-03 Initial
#:########################################################################################:#
# HARDWARE:
# - Zemismart KS-811 Single push button
# - BK7231N based switch
# - BK72XX board: generic-bk7231n-qfn32-tuya
# - BK7231N single-switch pin mapping:
#   - Button 1: P24
#   - Relay 1: P16
#   - Status LED: P22 inverted
#:########################################################################################:#
# OPERATION NOTES:
# - Main hallway nightlights switch with 1 output:
#   1) Main Hallway Nightlights
# - A single press of the physical button toggles the Main Hallway Nightlights group.
# - Home Assistant control of the exposed template switch also toggles the same group.
# - MQTT command topic requests On/Off changes.
# - Relay state changes publish retained status only.
# - Relays never publish command topics directly. This prevents command ping-pong/chattering.
# - This switch shares the mainhallway-nightlights command topic with Relay 2 on the
#   Main Hallway North Lightswitch.
#:########################################################################################:#
# MQTT COMMANDS:
# - ${mqtt_this_device_command_topic1}
#   Payloads: ${mqtt_command_on}, ${mqtt_command_off}
# - Status topic:
#   - ${mqtt_this_device_status_topic1}
#   Payloads mirror the actual relay state and are published retained.
#:########################################################################################:#
# OFFLINE NOTES:
# - a) Home Assistant offline (network and MQTT online):
#      - Local button control continues.
#      - MQTT command/status operation continues.
# - b) MQTT offline (or Home Assistant and MQTT offline):
#      - Local button control continues.
#      - If Home Assistant is online and connected by native ESPHome API, HA control can still
#        change the local relay but remote MQTT sync is unavailable until MQTT returns.
# - c) Entire WiFi/network offline:
#      - Local wall-switch button control continues.
#      - Accurate time is not needed. SNTP is not used for this device.
#:########################################################################################:#

#:########################################################################################:#
# 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-mainhallwaynightlights"
  friendly_name: "Main Hallway Nightlights (1)"
  description_comment: "BK7231N - Main Hallway Nightlights using a Zemismart KS-811 Single Push Button. Main Hallway Nightlights (1) (Layout V1.1)"
  device_area: "Main Hallway" # Allows ESP device to be automatically linked to an 'Area' in Home Assistant.

  # Project Naming
  project_name: "Zemismart Technologies.KS-811 Single BK7231N" # Project Details
  project_version: "v1.2" # Project V denotes release of yaml file, allowing checking of deployed vs latest version

  # Passwords
  api_key: !secret esp-api_key # unfortunately you can't use substitutions inside secrets names
  ota_pass: !secret esp-ota_pass # unfortunately you can't use substitutions inside secrets names
  static_ip_address: !secret esp-mainhallwaynightlights_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" # NONE, ERROR, WARN, INFO, DEBUG, VERBOSE, VERY_VERBOSE
  update_interval: "60s" # General sensor update interval

  # Default relay restore mode
  # Options: RESTORE_DEFAULT_OFF, RESTORE_DEFAULT_ON, ALWAYS_ON, ALWAYS_OFF
  relay_restore_mode: "RESTORE_DEFAULT_OFF"

  # MQTT Remote Controls
  mqtt_this_device_function1: "mainhallway-nightlights"
  mqtt_this_device_command_topic1: "${mqtt_command_main_topic}/${mqtt_this_device_function1}"
  mqtt_this_device_status_topic1: "${mqtt_status_main_topic}/${mqtt_this_device_function1}"
  mqtt_command_on: "On"
  mqtt_command_off: "Off"

  # Switch/Relay/Button Naming
  switch_1_name: "Main Hallway Nightlights"

#:########################################################################################:#
# 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}"

  #### WEB PORTAL ####
  #common_webportal: !include common/webportal_common.yaml

  #### SNTP (Only use if you want/need accurate timeclocks) ####
  #common_sntp: !include common/sntp_common.yaml

  #### 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

#:########################################################################################:#
# PACKAGE ITEM REMOVALS / OVERRIDES:
#:########################################################################################:#
wifi:
  min_auth_mode: !remove # The WPA2 only works on esp32/8266
  reboot_timeout: 15min  # override the selection from the common package
  fast_connect: false    # optional; force off in case package vars change

#mqtt:
#  keepalive: 15s # overriding the value from the common package

#:########################################################################################:#
# ESPHOME:
# https://esphome.io/components/esphome.html
#:########################################################################################:#
esphome:
  name: "${device_name}"
  friendly_name: "${friendly_name}"
  comment: "${description_comment}" # Appears on the ESPHome page in HA
  area: "${device_area}"
  project:
    name: "${project_name}"
    version: "${project_version}"

#:########################################################################################:#
# BUILD FOR BK CHIPSET:
# bk72xx
# https://devices.esphome.io/board/bk72xx/
#:########################################################################################:#
bk72xx:
  board: generic-bk7231n-qfn32-tuya

#:########################################################################################:#
# LOGGER:
# https://esphome.io/components/logger.html
#:########################################################################################:#
logger:
  level: "${log_level}" # INFO Level suggested, or DEBUG for testing
  baud_rate: 0 # set to 0 for no logging via UART, needed if you are using it for other serial things
  id: logger_id

#:########################################################################################:#
# MQTT: Device-specific MQTT command triggers
# This extends the common MQTT package with local custom command/status topics.
#:########################################################################################:#
mqtt:
  # Republish current relay state whenever MQTT reconnects so retained status stays accurate
  # for subscribers that reconnect later.
  on_connect:
    - mqtt.publish:
        topic: "${mqtt_this_device_status_topic1}"
        payload: !lambda |-
          return id(Relay_1).state ? "${mqtt_command_on}" : "${mqtt_command_off}";
        retain: true

  on_message:
    # Mirror remote command onto Relay_1, but only if a real state change is needed.
    # Do not republish the command from here. Relay status is published by the relay callback.
    - topic: "${mqtt_this_device_command_topic1}"
      payload: "${mqtt_command_on}"
      then:
        - if:
            condition:
              lambda: "return !id(Relay_1).state;"
            then:
              - switch.turn_on: Relay_1

    - topic: "${mqtt_this_device_command_topic1}"
      payload: "${mqtt_command_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: P22 # GPIO22
    inverted: true

#:########################################################################################:#
# SCRIPTS
# https://esphome.io/components/script.html
#:########################################################################################:#
script:
  # Local/HA requested ON. Change the local relay immediately, then publish a non-retained
  # command so the matching relay on the north switch follows.
  - id: mainhallway_nightlights_on
    mode: restart
    then:
      - if:
          condition:
            lambda: "return !id(Relay_1).state;"
          then:
            - switch.turn_on: Relay_1
      - mqtt.publish:
          topic: "${mqtt_this_device_command_topic1}"
          payload: "${mqtt_command_on}"
          retain: false

  # Local/HA requested OFF. Change the local relay immediately, then publish a non-retained
  # command so the matching relay on the north switch follows.
  - id: mainhallway_nightlights_off
    mode: restart
    then:
      - if:
          condition:
            lambda: "return id(Relay_1).state;"
          then:
            - switch.turn_off: Relay_1
      - mqtt.publish:
          topic: "${mqtt_this_device_command_topic1}"
          payload: "${mqtt_command_off}"
          retain: false

  # Physical button toggle logic. This is the only place that decides ON vs OFF.
  - id: mainhallway_nightlights_toggle
    mode: restart
    then:
      - if:
          condition:
            lambda: "return id(Relay_1).state;"
          then:
            - script.execute: mainhallway_nightlights_off
          else:
            - script.execute: mainhallway_nightlights_on

#:########################################################################################:#
# BINARY SENSORS:
# https://esphome.io/components/binary_sensor/
#:########################################################################################:#
binary_sensor:
  - platform: gpio
    id: button_1
    internal: true
    pin:
      number: P24 # GPIO24
      mode:
        input: true
        pullup: true
      inverted: true
    name: "Button 1: ${switch_1_name}"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
    on_press:
      - script.execute: mainhallway_nightlights_toggle

#:########################################################################################:#
# SWITCH COMPONENT:
# https://esphome.io/components/switch/
#:########################################################################################:#
switch:
  # Exposed Home Assistant control. This controls the local relay and publishes the matching
  # command so the Main Hallway North Lightswitch nightlights relay follows.
  - platform: template
    name: "Relay 1: ${switch_1_name}"
    id: Relay_1_HA
    lambda: |-
      return id(Relay_1).state;
    turn_on_action:
      - script.execute: mainhallway_nightlights_on
    turn_off_action:
      - script.execute: mainhallway_nightlights_off

  # Physical relay. 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: P16 # GPIO16
    id: Relay_1
    restore_mode: ${relay_restore_mode}
    on_turn_on:
      - mqtt.publish:
          topic: "${mqtt_this_device_status_topic1}"
          payload: "${mqtt_command_on}"
          retain: true
    on_turn_off:
      - mqtt.publish:
          topic: "${mqtt_this_device_status_topic1}"
          payload: "${mqtt_command_off}"
          retain: true