grahambrown11 / ESPAlarmPanel

Make my dumb alarm panel smart
0 stars 0 forks source link

New Alarm Panel ESPHome #1

Closed NonaSuomy closed 1 year ago

NonaSuomy commented 1 year ago

Hi there,

Seems to have a typo in the example

https://esphome.io/components/alarm_control_panel/template.html

[arming_time] is an invalid option for [alarm_control_panel.template]. Did you mean [arming_home_time], [arming_away_time], [trigger_time]?

Another question my contacts and sensors have Double End Of Line Resistors and are detected by analog ads1115. They have 4 states Ok/Alarm/Short/Tamper is there a way to support this seems only binary sensors are supported?

Thank you.

grahambrown11 commented 1 year ago

Thanks, I'll submit a PR to correct the documentation, this project however is not the same as the ESPHome Alarm Control Panel, I will be converting this to extend that in the future... The alarm only supports binary sensors but you can use the binary lambda template to convert the 4 states you have into a simple on/off state.

NonaSuomy commented 1 year ago

I was looking for your contact but couldn’t find it. So was hoping this would be close enough :) Also missing id: acp1 above name the log debug statement is looking for it.

That’s what I’m doing converting it to two binary sensors on for open/close the other for tamper+short/no issue but just felt a little over coding. I just saw a conversation where you were talking to Jesse and saying most alarms are binary… where actually most alarm sensors are analog and require end of the line resistors that communicate more than binary states to the alarm system as a trouble warning when there is a short or cut as well as open/close states.

Thank you for making this component I just got it running making an analog ads1115 i2c board for it so it can detect the contacts properly.

3BB74109-AC90-4B1C-9010-793071D83682 C4FF5367-202E-4314-936E-68365ED164BF 965B0F6F-FD1F-472A-86D3-956E52BA168F 1A67B8D2-686B-49BD-92C4-DD836C36AC83

1U rack mountable PoE 16 zones

grahambrown11 commented 1 year ago

@NonaSuomy that looks like a cool project.

I wanted to start out simple to get the basics done, then once released (just waiting for the HA 2023.7) I'd start building out more. The basics were actually a lot more complicated than anticipated as the ESPHome to HomeAssistant integration has a lot of parts.

I'm not sure I agree with your statement "most alarm sensors are analog" they are actually binary - a door sensor is just a magnetic read switch and a PIR is also just a switch. I understand you can get fancy and instead of just understanding if the switch is open or closed you can add end of line resistors and measure voltage in case the wires were shorted, but at the end of the day that turns into a binary event that must either trigger the alarm or not 😉 Personally I think end of line resistors for a home installation is overkill and in most cases the installers are lazy and don't put the resistors at the end they put them all in the control box so they offer no value really...

Anyway ESPHome still offers the flexibility you can make binary sensors with the template platform so I see little to no need for me to add analog sensors to the alarm, you can still build an over engineered alarm system with all the components in ESPHome, just requires a little more config... 😉

NonaSuomy commented 1 year ago

Thank you. I'm having fun! Makes sense to start out simple! Currently using the HA 2023.7 to get your code working. I hope you keep on going. Love what you are doing so far! I understand how complex the system is between ESPHome/HA I've attempted multiple times to make things that didn't exist. It was always a daunting task to figure out for me and usually never came to fruition. So I really understand how great it is for how far you have got to this point!

https://ipvm.com/forums/video-surveillance/topics/where-do-end-of-line-resistors-go-80cda920-2b62-4ac4-8f1e-0d283a7de0ff

I don't believe it is over-engineering at all. Most home systems are installed by quick flyby night people not trained properly. Installing the EOL in the panel is only to save the cost of labor on the job so they can do the job quicker and cheaper. Hopefully, if they are installed in the panel they use 2 pairs to make them function properly and not just disable the function. It’s kind of a seedy practice as you are trusting a professional to do the correct job and they sneakily don’t. You as the unaware customer don’t know the wiser until something bad happens: 

There are a lot of home related, non-industrial/corp safety issues that are limited by that binary-only concept. I have not seen any real alarm system at home or otherwise without the analog function of reading EOL circa 25 years ago. Besides the new age Konnected platform. They added a second platform after the fact to support systems directly with EOL and change them to output binary values. IoT companies jump into the ring not understanding the full breadth of what they are doing. Alarm systems do have abilities now to disable EOL functions but I wouldn't promote that practice. Ideally, still, in real alarm systems, the terminal is analog no matter how you disable that security function with software or hardware. Most systems also now auto-detect the end-of-the-line resistance values like a learning state so you don't have to touch settings. It would be neat if eventually, you could add this learning state if there was EOL installed. Fire sensors are actually legally required to have a 4.7Kohm resistor at the end of the line. In some countries.

This is basically why the device class "tamper" exists in ESPHome/Ha. Here is the conversion I currently did:

binary_sensor:
  - platform: template
    name: zone_01
    id: zone_01
    device_class: door
    lambda: return (id(zone_01_str).state != "OK");
  - platform: template
    name: zone_01_tamper
    id: zone_01_tamper
    device_class: tamper
    lambda: |-
      if (id(zone_01_str).state == "Tamper") || (id(zone_01_str).state == "Short") {
        return true;
      } else {
        return false;
      }

Here is what I was working with. Still learning and figuring it out. It functions so far :D

It just seems like a lot of duplicated YAML for reading analog values and then converting them to binary sensors. Feels like it should just be handled in the backend and the user does not have to think about it. Food for future thought.

#TTGO T-Internet PoE 1.0 I2C to Arduino I2C Expander

# GPIO39 GPIO36
# GPIO35 GPIO34
# GPIO32 GPIO16
# GPIO12 GPIO33
# GPIO15 GPIO04
# GPIO14 GPIO02
#    GND M3V3
#    GND M3V3

#PIN Header:
# IO34 IO35
# IO16 IO32
# IO33 IO12
# IO04 IO15
# IO32 IO14

# Internet Module HR861153C
#  RXD0 IO25
#  RXD1 IO26
# CRCDV IO27
#   MDC IO23
#  TXD1 IO22
# TX_EN IO21
#  TXD0 IO19
# MIDIO IO18

# TF Card
# MOSI IO12
# SCLK IO15
#   CS IO13
# MISO IO02

# BME280
# SDI
# SCK
# GND
# VCC(5V)

#GPIO33 IR Emitter
#GPIO35 IR Receiver

esphome:
  name: ttgo-poe-001
  comment: TTGO T-Internet PoE 1.0
  friendly_name: ttgo-poe-001
#  includes:
#    - arduino_port_expander.h

esp32:
  board: esp32-poe
  #board: esp32dev
  framework:
    type: arduino

#external_components:
#  - source: github://pr#4243
#    components:
#      - ens160

# Define I2C device
# Arduino SDA A4
#         SCL A5
# ESP8266 SDA D2
#         SCL D1
# ESP32   SDA 33
#         SCL 32
i2c:
  id: i2c_component
  sda: 33
  scl: 32
  scan: true

# IR receiver pin setup.
#remote_receiver:
#  pin: 
#    number: GPIO35
#    inverted: True
#  dump: all

# IR transmitter pin setup.
#remote_transmitter:
#  pin: GPIO33
  # Infrared remotes use a 50% carrier signal
#  carrier_duty_percent: 50%

# Enable logging
logger:
  #level: VERY_VERBOSE
  logs:
    ads1115: ERROR
#  level: INFO

#ethernet:
#  type: LAN8720
#  mdc_pin: GPIO23
#  mdio_pin: GPIO18
#  clk_mode: GPIO17_OUT
#  phy_addr: 0
#  use_address: !secret use_address_eth004

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  use_address: !secret use_address_wifi004

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "TTGO-Poe-001 Fallback Hotspot"
    password: !secret fallbackhotspot004

# Enable Home Assistant API
api:
  encryption:
    key: !secret encryption_key004

ota:
  password: !secret ota_pass004

improv_serial:

captive_portal:

web_server:

substitutions:
# Common ADS1115 Sensor Setting
  ads_gain: "6.144"
  ads_update_interval: "500ms"
  ads_delta: "0.2"
  ads_heartbeat: "120s"

# Common Text Sensor Setting
  zone_icon: "mdi:lock-alert"

# Common Zone (Int) Sensor Setting
  sensor_icon: "mdi:shield-lock"

# Neopixel Brightness
# led_bright: "50%"

# Transition Voltages between States
# 0 -> (State1) -> V1 -> (State2) -> V2 -> (State3) -> V3 -> (State4)
  v1: "1"
  v2: "1.8"
  v3: "2.5"

# Text Sensor strings for each state
  alarm_state1: "Short"
  alarm_state2: "OK"
  alarm_state3: "Alarm"
  alarm_state4: "Tamper"

# Led Effect (Appended to led name for Custom Effect)
#  led_effect1: "RED_BLINK_FAST"
#  led_effect2: "GREEN"
#  led_effect3: "RED"
#  led_effect4: "RED_BLINK_FAST"

# ADS1115 Analog to Digital setup.
ads1115:
  # ADS1115 (ADDR => Ground)
  - address: 0x48
    id: ads1115_48
  # ADS1115 (ADDR => VDD)
  #- address: 0x49
  #  id: ads1115_49

alarm_control_panel:
  platform: template
  id: acp1
  name: Alarm Panel
  codes:
    - "1234"
  requires_code_to_arm: true
  arming_away_time: 10s
  arming_home_time: 10s
  pending_time: 15s
  trigger_time: 5min
  binary_sensors:
    - input: zone_01
    - input: zone_02
    - input: zone_03
    - input: zone_04
      bypass_armed_home: true
    #- input: ha_test
  on_state:
    then:
      - lambda: !lambda |-
          ESP_LOGD("TEST", "State change %s", alarm_control_panel_state_to_string(id(acp1)->get_state()));
  on_triggered:
    then:
      #- switch.turn_on: siren_inside
      - switch.turn_on: siren_outside
  on_cleared:
    then:
      #- switch.turn_off: siren_inside
      - switch.turn_off: siren_outside

binary_sensor:
  - platform: template
    name: Zone 01
    id: zone_01
    device_class: door
    lambda: return (id(zone_01_str).state != "OK");
  - platform: template
    name: Zone 01 Tamper
    id: zone_01_tamper
    device_class: tamper
    lambda: |-
      if ((id(zone_01_str).state == "Tamper") || (id(zone_01_str).state == "Short")) {
        return true;
      } else {
        return false;
      }
  - platform: template
    name: Zone 02
    id: zone_02
    device_class: door
    lambda: return (id(zone_02_str).state != "OK");
  - platform: template
    name: Zone 02 Tamper
    id: zone_02_tamper
    device_class: tamper
    lambda: |-
      if ((id(zone_02_str).state == "Tamper") || (id(zone_02_str).state == "Short")) {
        return true;
      } else {
        return false;
      }
  - platform: template
    name: Zone 03
    id: zone_03
    device_class: door
    lambda: return (id(zone_03_str).state != "OK");
  - platform: template
    name: Zone 03 Tamper
    id: zone_03_tamper
    device_class: tamper
    lambda: |-
      if ((id(zone_03_str).state == "Tamper") || (id(zone_03_str).state == "Short")) {
        return true;
      } else {
        return false;
      }
  - platform: template
    name: Zone 04
    id: zone_04
    device_class: window
    lambda: return (id(zone_04_str).state != "OK");
  - platform: template
    name: Zone 04 Tamper
    id: zone_04_tamper
    device_class: tamper
    lambda: |-
      if ((id(zone_04_str).state == "Tamper") || (id(zone_04_str).state == "Short")) {
        return true;
      } else {
        return false;
      }
#  - platform: gpio
#    id: zone_1
#    name: Zone 1
#    device_class: door
#    pin:
#      number: 14
#      mode: INPUT_PULLUP
#      inverted: True
#  - platform: gpio
#    id: zone_2
#    name: Zone 2
#    device_class: motion
#    pin:
#      number: 16
#      mode: INPUT_PULLUP
#      inverted: True
#  - platform: homeassistant
#    id: ha_test
#    name: Zone 3
#    entity_id: input_boolean.test_switch

switch:
  - platform: restart
    name: "T-PoE Restart"
  #- platform: gpio
    #id: siren_inside
    #name: Siren Inside
    #icon: mdi:alarm-bell
    #pin: 04
  - platform: gpio
    id: siren_outside
    name: Siren Outside
    icon: mdi:alarm-bell
    pin: 16

# Define binary sensors, use the Arduino PIN number for digital pins and
# for analog use 14 for A0, 15 for A1, and so on...
#binary_sensor:
#  - platform: gpio
#    id: sw_lights_enable
#    name: Lights Enable
#    pin:
#      number: GPIO13
#    internal: true
#    on_state:
#      if:
#        condition:
#          binary_sensor.is_on: sw_lights_enable
#        then:
#          # Publish Current Zone States to force update of effect
#          - lambda: |-
#              id(zone_01_int).publish_state(id(zone_01_int).state);
#        else:
#          - light.turn_off: led_01

# NEOPixel Leds. 1 String with 1 LED / Zone via partition
#light:
#  # Define Parent String
#  - platform: fastled_clockless
#    chipset: WS2812B
#    internal: true
#    rgb_order: GRB
#    default_transition_length: 0s
#    pin: GPIO32
#    color_correct : 
#      - ${led_bright}
#      - ${led_bright}
#      - ${led_bright}
#    num_leds: 12
#    name: "LED_Status_String"
#    id: led_string

    # 1 Partition (Indicator LED) per Zone
    # Zone 1
#  - platform: partition
#    id: led_01
#    internal: true
#    name: "Zone 1 LED"
#    default_transition_length: 0s
#    segments:
#      - id: led_string
#        from: 0
#        to: 0
    # Include Common Effects - RED/GREEN/BLINK_RED_SLOW/BLINK_RED_FAST
    #effects: !include include/esp_include_alarm_led_effect.yaml

# Define analog sensors
sensor:
  - platform: uptime
    name: Uptime Sensor

    # Integer State Sensors, Controls Zone Text Sensor and Zone Indicator LED Effect
  - platform: template
    name: "Zone 01 Sensor"
    id: zone_01_int
    icon: "${sensor_icon}"
    on_value:
      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
      - lambda: |-
          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
          int y = (int) x;
          id(zone_01_str).publish_state(out_state[y]);
  - platform: template
    name: "Zone 02 Sensor"
    id: zone_02_int
    icon: "${sensor_icon}"
    on_value:
      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
      - lambda: |-
          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
          int y = (int) x;
          id(zone_02_str).publish_state(out_state[y]);
  - platform: template
    name: "Zone 03 Sensor"
    id: zone_03_int
    icon: "${sensor_icon}"
    on_value:
      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
      - lambda: |-
          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
          int y = (int) x;
          id(zone_03_str).publish_state(out_state[y]);
  - platform: template
    name: "Zone 04 Sensor"
    id: zone_04_int
    icon: "${sensor_icon}"
    on_value:
      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
      - lambda: |-
          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
          int y = (int) x;
          id(zone_04_str).publish_state(out_state[y]);
#  - platform: template
#    name: "Zone05 Sensor"
#    id: zone_05_int
#    icon: "${sensor_icon}"
#    on_value:
#      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
#      - lambda: |-
#          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
#          int y = (int) x;
#          id(zone_05_str).publish_state(out_state[y]);
#  - platform: template
#    name: "Zone06 Sensor"
#    id: zone_06_int
#    icon: "${sensor_icon}"
#    on_value:
#      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
#      - lambda: |-
#          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
#          int y = (int) x;
#          id(zone_06_str).publish_state(out_state[y]);
#  - platform: template
#    name: "Zone07 Sensor"
#    id: zone_07_int
#    icon: "${sensor_icon}"
#    on_value:
#      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
#      - lambda: |-
#          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
#          int y = (int) x;
#          id(zone_07_str).publish_state(out_state[y]);
#  - platform: template
#    name: "Zone08 Sensor"
#    id: zone_08_int
#    icon: "${sensor_icon}"
#    on_value:
#      # When Zone state (1-4) changes, set effect if Lights enabled, Set Text State
#      - lambda: |-
#          std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
#          int y = (int) x;
#          id(zone_08_str).publish_state(out_state[y]);

  - name: "Zone 01 Voltage"
    id: ads1115_48_a0
    ads1115_id: ads1115_48
    platform: ads1115
    multiplexer: 'A0_GND'
    resolution: 16_BITS
    gain: ${ads_gain}
    update_interval: ${ads_update_interval}
    internal: false
    filters:
      - or:
        - delta: ${ads_delta}
        - heartbeat: ${ads_heartbeat}
    on_value:
      then:
        # Compare Reading to Ref voltages and set zone state (Int) if changed.
        - lambda: |-
            int y = 0;
            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red_Blink
            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
            if (id(zone_01_int).state != y) {
              id(zone_01_int).publish_state(y);
            }
  - name: "Zone 02 Voltage"
    id: ads1115_48_a1
    ads1115_id: ads1115_48
    platform: ads1115
    multiplexer: 'A1_GND'
    resolution: 16_BITS
    gain: ${ads_gain}
    update_interval: ${ads_update_interval}
    internal: false
    filters:
      - or:
        - delta: ${ads_delta}
        - heartbeat: ${ads_heartbeat}
    on_value:
      then:
        # Compare Reading to Ref voltages and set zone state (Int) if changed.
        - lambda: |-
            int y = 0;
            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red_Blink
            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
            if (id(zone_02_int).state != y) {
              id(zone_02_int).publish_state(y);
            }
  - name: "Zone 03 Voltage"
    id: ads1115_48_a2
    ads1115_id: ads1115_48
    platform: ads1115
    multiplexer: 'A2_GND'
    resolution: 16_BITS
    gain: ${ads_gain}
    update_interval: ${ads_update_interval}
    internal: false
    filters:
      - or:
        - delta: ${ads_delta}
        - heartbeat: ${ads_heartbeat}
    on_value:
      then:
        # Compare Reading to Ref voltages and set zone state (Int) if changed.
        - lambda: |-
            int y = 0;
            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red_Blink
            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
            if (id(zone_03_int).state != y) {
              id(zone_03_int).publish_state(y);
            }
  - name: "Zone 04 Voltage"
    id: ads1115_48_a3
    ads1115_id: ads1115_48
    platform: ads1115
    multiplexer: 'A3_GND'
    resolution: 16_BITS
    gain: ${ads_gain}
    update_interval: ${ads_update_interval}
    internal: false
    filters:
      - or:
        - delta: ${ads_delta}
        - heartbeat: ${ads_heartbeat}
    on_value:
      then:
        # Compare Reading to Ref voltages and set zone state (Int) if changed.
        - lambda: |-
            int y = 0;
            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red_Blink
            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
            if (id(zone_04_int).state != y) {
              id(zone_04_int).publish_state(y);
            }
#  - name: "Zone05 Voltage"
#    id: ads1115_49_a0
#    ads1115_id: ads1115_49
#    platform: ads1115
#    multiplexer: 'A0_GND'
#    resolution: 16_BITS
#    gain: ${ads_gain}
#    update_interval: ${ads_update_interval}
#    internal: false
#    filters:
#      - or:
#        - delta: ${ads_delta}
#        - heartbeat: ${ads_heartbeat}
#    on_value:
#      then:
#        # Compare Reading to Ref voltages and set zone state (Int) if changed.
#        - lambda: |-
#            int y = 0;
#            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red_Blink
#            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
#            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
#            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
#            if (id(zone_05_int).state != y) {
#              id(zone_05_int).publish_state(y);
#            }
#  - name: "Zone06 Voltage"
#    id: ads1115_49_a1
#    ads1115_id: ads1115_49
#    platform: ads1115
#    multiplexer: 'A1_GND'
#    resolution: 16_BITS
#    gain: ${ads_gain}
#    update_interval: ${ads_update_interval}
#    internal: false
#    filters:
#      - or:
#        - delta: ${ads_delta}
#        - heartbeat: ${ads_heartbeat}
#    on_value:
#      then:
#        # Compare Reading to Ref voltages and set zone state (Int) if changed.
#        - lambda: |-
#            int y = 0;
#            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red_Blink
#            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
#            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
#            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
#            if (id(zone_06_int).state != y) {
#              id(zone_06_int).publish_state(y);
#            }
#  - name: "Zone07 Voltage"
#    id: ads1115_49_a2
#    ads1115_id: ads1115_49
#    platform: ads1115
#    multiplexer: 'A2_GND'
#    resolution: 16_BITS
#    gain: ${ads_gain}
#    update_interval: ${ads_update_interval}
#    internal: false
#    filters:
#      - or:
#        - delta: ${ads_delta}
#        - heartbeat: ${ads_heartbeat}
#    on_value:
#      then:
#        # Compare Reading to Ref voltages and set zone state (Int) if changed.
#        - lambda: |-
#            int y = 0;
#            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red_Blink
#            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
#            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
#            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
#            if (id(zone_07_int).state != y) {
#              id(zone_07_int).publish_state(y);
#            }
#  - name: "Zone08 Voltage"
#    id: ads1115_49_a3
#    ads1115_id: ads1115_49
#    platform: ads1115
#    multiplexer: 'A3_GND'
#    resolution: 16_BITS
#    gain: ${ads_gain}
#    update_interval: ${ads_update_interval}
#    internal: false
#    filters:
#      - or:
#        - delta: ${ads_delta}
#        - heartbeat: ${ads_heartbeat}
#    on_value:
#      then:
#        # Compare Reading to Ref voltages and set zone state (Int) if changed.
#        - lambda: |-
#            int y = 0;
#            if (x > ${v3}) { y = 4; }                     // # State 4 = Tamper/Short = Red Blink
#            else if (x > ${v2} && x <= ${v3}) { y = 3; }  // # State 3 = Alarm = Red
#            else if (x > ${v1} && x <= ${v2}) { y = 2; }  // # State 2 = OK = Green
#            else if (x <= ${v1}) { y = 1; }               // # State 1 = Short = Red Blink
#            if (id(zone_08_int).state != y) {
#              id(zone_08_int).publish_state(y);
#            }
# Text Sensors visible to HA, 1 per Zone
text_sensor:
  - platform: template
    name: "Zone 01 State"
    icon: "${zone_icon}"
    id: zone_01_str
  - platform: template
    name: "Zone 02 State"
    icon: "${zone_icon}"
    id: zone_02_str
  - platform: template
    name: "Zone 03 State"
    icon: "${zone_icon}"
    id: zone_03_str
  - platform: template
    name: "Zone 04 State"
    icon: "${zone_icon}"
    id: zone_04_str
#  - platform: template
#    name: "Zone05 State"
#    icon: "${zone_icon}"
#    id: zone_05_str
#  - platform: template
#    name: "Zone06 State"
#    icon: "${zone_icon}"
#    id: zone_06_str
#  - platform: template
#    name: "Zone07 State"
#    icon: "${zone_icon}"
#    id: zone_07_str
#  - platform: template
#    name: "Zone08 State"
#    icon: "${zone_icon}"
#    id: zone_08_str

HA Cards

type: grid
square: false
columns: 1
cards:
  - type: alarm-panel
    entity: alarm_control_panel.ttgo_poe_001_alarm_panel
    name: PoE Alarm Control Panel
    states:
      - arm_home
      - arm_away
      - arm_night
      - arm_vacation
      - arm_custom_bypass
  - type: entities
    entities:
      - entity: switch.ttgo_poe_001_siren_inside
      - entity: switch.ttgo_poe_001_siren_outside
      - entity: binary_sensor.ttgo_poe_001_zone_01
      - entity: binary_sensor.ttgo_poe_001_zone_01_tamper
      - entity: binary_sensor.ttgo_poe_001_zone_02
      - entity: binary_sensor.ttgo_poe_001_zone_02_tamper
      - entity: binary_sensor.ttgo_poe_001_zone_03
      - entity: binary_sensor.ttgo_poe_001_zone_03_tamper
      - entity: binary_sensor.ttgo_poe_001_zone_04
      - entity: binary_sensor.ttgo_poe_001_zone_04_tamper

image

image

Hope you keep going with this great work so far!