Have just completed a project thats been in the plans for a while. Final implementation was actually remarkably straight forward all thanks to how quick,easy and flexible ESP Home is.
I wired the house with a “Comfort” home alarm/home automation system 18?+ years ago. At the time I interfaced it through homeseer and had a number of automations controlling X10 lights etc… In practice its been unused for many years. However I wanted to re-use the wired PIRs for general occupancy detection. As “proper” alarm zones they have End of Line (EOL) resistors to detect the differences between open circuit, short circuit, alarm and Ok for each zone. To detect motion - you need to measure the voltage (resistance) on each PIR Zone rather than just detect open/close.
There is at least one replacement alarm panel - which casually dismisses the EOL resistors and tells you to remove them. I didn’t fancy opening up every PIR to remove them…and they’re actually very useful in detecting wiring faults etc. even if just for HA rather than security. A quick google turns up several circuits though. This Page in particular is excellent in explaining how they work - and how to detect the various zone states with an arduino and an A to D channel.
A check of the various ESPHome supported sensors and I found the ADS1115. 4 A to D channels…and up to 4 board supported. Upto 16 A to D channels for only 2 ESP pins. A couple of boards ordered (I wanted 8 zones) and a very easy breadboard and I proved the principle of detecting the voltage in the various states. Monitoring the actual recorded voltage with a specific set of resistors makes it possible to the then detect the state reliably. In ESP home - each channel on the ADS1115 reflects a zone and gets its own sensor for the voltage. This stays internal, but when it changes 2 other template sensors are updated according to the voltage. An integer sensor reflecting the state (1=Short,2=Alarm,3=OK,4=Open/Tamper) and a string with the state name. Both are exposed to HA currently (not sure in practice which I will use in automations).
I also wanted the panel to be able to show the state of each zone - so a string of neopixels is used. Each led reflects a zone. The partition function is used so each zone has a light (1 led) but all is driven off a single pin. A common set of effects (green, red, blinking etc) is inlcuded for each light. Not the most memory efficient…but it works very well.
And because i dont always need the LEDs flashing away - there’s a capacative touch switch (because I had some!) that is used to enable/disable the led updates. Also added a temp sensor (DHT11) because…why not.
I have a couple of spare GPIO wired for a couple of other flow switches I want to use aswell.
The PIRs need 12V and the D1 mini is 5V, so I used a spare small PC psu and a break out board that gives the 12/5/3.3. All mounted in the original alarm enclosure. I mounted the D1 mini, DHT11, ADS1115 etc on some perfboard which made the wiring very easy, including screw terminal blocks for the PIRs. Also included some 12v distribution terminal blocks aswell.
There’s clearly a lot of repetition with an LED, ADS1115 and 2 template sensors per zone in the YAML - but actually the meat of the code is straightforward.
Full YAML is Here
Key elements for a single zone are below (not a complete running yaml!) with a few pics.
So far all working very well - and just re-inforces how easy ESPHome + HA makes this. Now to actually do something useful with the motion detection !.
Original panel, including backup battery…
Breadboarding test…
Mounting the ESP in the panel
Fitted and working…
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"
# Transition Voltages between States
# 0 -> (State1) -> V1 -> (State2) -> V2 -> (State3) -> V3 -> (State4)
v1: "1"
v2: "2.5"
v3: "3.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"
# I2C Settings for 2 x ADS1115
i2c:
sda: GPIO4
scl: GPIO5
scan: False
ads1115:
# 1st ADS1115 (ADDR -> Grnd)
- address: 0x48
id: ads1115_1
# 2nd ADS1115 (ADDR -> VDD)
- address: 0x49
id: ads1115_2
# 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: GPIO15
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
# Zone 2
sensor:
- platform: template
name: "Zone01 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_effect[] = {"Unset","${led_effect1}","${led_effect2}","${led_effect3}","${led_effect4}" };
std::string out_state[] = {"Unset","${alarm_state1}","${alarm_state2}","${alarm_state3}","${alarm_state4}" };
int y = (int) x;
if (id(sw_lights_enable).state) {
auto call = id(led_01).turn_on();
call.set_effect(out_effect[y]);
call.perform();
}
id(zone_01_str).publish_state(out_state[y]);
# ADS1115 Voltage Sensors (1 per Zone)
- platform: ads1115
ads1115_id: ads1115_1
multiplexer: 'A0_GND'
gain: ${ads_gain}
name: "ADS1115-1-A0"
id: ads1115_1_a0
update_interval: ${ads_update_interval}
internal: true
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);
}
text_sensor:
- platform: template
name: "Zone01 State"
icon: "${zone_icon}"
id: zone_01_str