As I alluded to above, the templates only poll their template values with a default of 60s. You can reduce the poll rate, but this isn’t ideal.
I updated the gist with my current code. The gobal integer restore on the esp32 works well after a hard power reset. I found that the update to the global integer introduced a small but noticeable lag when trying to observe state update, so it seems to work better when it is the last action in a sequence.
I made significant changes to your project … Thanks for sharing it … As soon as possible I create a new post with everything you need to make it happen … By sharing the link in this topic. See you soon
I would love to see.
My PR to include pending in the possible states for the template alarm control panel is available in v0.108. now there is no longer an error temporarily.
I anticipate the news. Addition of led ws2812b to indicate the node status, and specifically the status LED of the on / off sensors at the passage if disarmed, while fixed if armed and in alarm to identify the sensors that have detected the presence, LED for API connection status and wifi, armed / disarmed alarm status led, triggered status led flashing and fixed in alarm. All this even in case of recovery
I’m doing something very similar to this, though not as complex. Thread here. I’m also using an ESP32 but I’m using a TTGO T-Call board with integrated SIM800L so I can text arm/disarm and get notifications if it triggers. Basically my kit is all over the house so I can’t UPS it all so I also need a self contained alarm panel in the event the perp flicks off the power to the house.
Just lately I’ve lost interest as it’s gotten way more complex than I’d hoped and I’ve run into this conundrum as well. Are you saying that it seems to reconnect to wifi OK without the board rebooting? I haven’t had the chance to test. Mine’s coded to trigger the relay for the sirens/strobes then silence the sirens after 20 minutes. The board is connected to the battery backup of the alarm panel so power outs are not an issue but obviously wifi drops are. If you’re getting reliable reconnections, why do you need to restore states/in what scenario will your board power cycle? Can’t you just set reboot_timeout to 0 and forget about it?
Thanks for sharing.
The use case is if I go on vacation and the power goes out long enough to deplete the backup battery. Then, the system reboots into the correct state when power returns.
Got it - so you’re looking for it to stay armed in this situation. Note your bell box will likely have sung itself to death in this scenario - hopefully any neighbours haven’t decided to ignore if it goes off again!
I reckon I’ll have mine restore armed/disarmed state but not triggered state and will set the reboot_timeout to something a bit longer than the default 15 mins, just to reduce the likelihood of it resetting and turning off the triggered state soon after it becoming triggered.
Mine presents template switches for armed/disarmed and triggered. I haven’t really thought about how to keep both HA and the ESP’s armed/disarmed state in sync yet.
My code restores whatever state it was on prior to power cut. The alarm siren won’t go off unless it was previously going off. I also have a timer for the alarm siren so it stops after x hrs. If my neighbors don’t call me by then…
If you don’t want it to reset into triggered, reset it into armed and it can retrigger if the disturbance is still detected. I thought about this too, and in fact, it may be what I have programmed on my live system.
You can use a template alarm control panel.
I haven’t yet gotten my head around the template alarm control panel or why I would need it - right now I’ve got a manual alarm control panel set up.
Thanks for that - I’ve now implemented similar, however I have my strobe set up to restore state and continue indefinitely, until the system is disarmed.
So in the scenario that your panel loses AC and its battery goes dead (hence the need for restore states) your bell box won’t go off/flatten itself? Other thing - Are you UK based? If so, heads up - I’m told that bell boxes shouldn’t ring more than 20 minutes to avoid nuisance. Apparently neighbours/the council are within their rights to disconnect/smash it off the wall if it’s going off longer!
For anyone interested, my code below (it’s come on a bit since my other thread, ‘Virtual Siren/Strobe’ are temporary and just for testing).
esphome:
name: tcall
platform: ESP32
board: esp32dev
wifi:
ssid: !secret ssid
password: !secret wpa2
reboot_timeout: 60min
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "tcall Fallback Hotspot"
password: !secret hotspotpass
captive_portal:
# Enable logging
logger:
# Enable Home Assistant API
api:
password: !secret apipass
reboot_timeout: 60min
services:
- service: send_sms
variables:
recipient: string
message: string
then:
- sim800l.send_sms:
recipient: !lambda 'return recipient;'
message: !lambda 'return message;'
ota:
password: !secret apipass
switch:
- platform: gpio
id: "SIM800_PWKEY"
pin: 4
restore_mode: ALWAYS_OFF
- platform: gpio
id: "SIM800_RST"
pin: 5
restore_mode: ALWAYS_ON
- platform: gpio
id: "SIM800_POWER"
pin: 23
restore_mode: ALWAYS_ON
- platform: gpio
inverted: true
pin: GPIO33
name: "Siren Relay"
id: "siren"
restore_mode: always_off
- platform: gpio
inverted: false
pin: GPIO32
name: "Strobe Relay"
id: "strobe"
restore_mode: restore_default_off
- platform: template
id: "armdisarm"
name: "Arm/Disarm"
restore_state: true
optimistic: true
on_turn_off:
then:
- switch.turn_off: "alarmtriggered"
- switch.turn_off: "vstrobe"
- platform: template
id: "alarmtriggered"
name: "Alarm Triggered"
restore_state: false
optimistic: true
on_turn_on:
- if:
condition:
- wifi.connected:
then:
else:
- sim800l.send_sms:
recipient: !secret mobjr
message: ALARM HAS BEEN TRIGGERED! WIFI CONNECTION UNAVAILABLE. REPLY DISARM TO STOP.
- switch.turn_on: "vsiren"
- switch.turn_on: "vstrobe"
- delay: 20min
- switch.turn_off: "alarmtriggered"
on_turn_off:
- switch.turn_off: "vsiren"
- platform: template
id: "vsiren"
name: "Virtual Siren"
restore_state: false
optimistic: true
- platform: template
id: "vstrobe"
name: "Virtual Strobe"
restore_state: true
optimistic: true
uart:
baud_rate: 9600
tx_pin: 27
rx_pin: 26
sim800l:
on_sms_received:
- lambda: |-
id(sms_sender).publish_state(sender);
id(sms_message).publish_state(message);
text_sensor:
- platform: template
id: sms_sender
name: "SMS Sender"
- platform: template
id: sms_message
name: "SMS Message"
on_value:
- if:
condition:
and:
- text_sensor.state:
id: sms_message
state: 'ARM'
- text_sensor.state:
id: sms_sender
state: !secret mobjr
then:
- sim800l.send_sms:
recipient: !secret mobjr
message: System armed successfully.
- switch.turn_on: "armdisarm"
- delay: 30s
- text_sensor.template.publish:
id: sms_sender
state: "Erased"
- text_sensor.template.publish:
id: sms_message
state: "Erased"
- if:
condition:
and:
- text_sensor.state:
id: sms_message
state: 'DISARM'
- text_sensor.state:
id: sms_sender
state: !secret mobjr
then:
- sim800l.send_sms:
recipient: !secret mobjr
message: System disarmed successfully.
- switch.turn_off: "armdisarm"
- delay: 30s
- text_sensor.template.publish:
id: sms_sender
state: "Erased"
- text_sensor.template.publish:
id: sms_message
state: "Erased"
binary_sensor:
- platform: gpio
name: "AC Power Detector"
id: acpower
pin:
number: GPIO25
inverted: true
device_class: power
on_state:
then:
- if:
condition:
and:
- binary_sensor.is_off: "acpower"
- switch.is_on: "armdisarm"
then:
- sim800l.send_sms:
recipient: !secret mobjr
message: AC POWER FAILURE. ALARM STILL ARMED. GARAGE UNPROTECTED. TEXT DISARM TO DISARM.
- if:
condition:
and:
- binary_sensor.is_off: "acpower"
- switch.is_off: "armdisarm"
then:
- sim800l.send_sms:
recipient: !secret mobjr
message: AC power failure. To arm alarm in power-off state, text ARM.
- if:
condition:
- binary_sensor.is_on: "acpower"
then:
- sim800l.send_sms:
recipient: !secret mobjr
message: Power restored. Normal operation will be resumed shortly.
- platform: gpio
name: "Lounge PIR"
pin:
number: GPIO19
mode: INPUT_PULLUP
device_class: motion
on_press:
then:
- if:
condition:
- switch.is_on: "armdisarm"
then:
- switch.turn_on: "alarmtriggered"
- platform: gpio
name: "Hall PIR"
pin:
number: GPIO13
mode: INPUT_PULLUP
device_class: motion
on_press:
then:
- if:
condition:
- switch.is_on: "armdisarm"
then:
- switch.turn_on: "alarmtriggered"
You have the alarm logic in HA then in some form. I have the alarm logic in the esp unit and the template alarm control panel is just a representation of the alarm system that allows you to interact with it, but there is no alarm logic.
I’ve programmed my system to not turn on the siren in this case. My old commercial system worked the same way, the power for the bell shared power work the panel, so when the battery died, everything is down.
I can turn my alarm on and off remotely through the wonders of home assistant!
Aaaah got it, thanks!
Wow Great Project…
I have some question, If you make it with tm1638, Pin is available to make it.
But tm1638 custom component should be made for ESPHOME.
thx again for your great project sharing.
In the initial code which I thank for sharing, an important condition has not been implemented. If a door or window is open or some PIR sensor is active because someone is still at home, and the alarm is armed, it will enter an armed state because to activate the trigger it is expected the change of state. I modified qursto by checking the state of the sensors. If these are in the on state because I use on_state and not on_press the alarm enters the delay by triggering the siren, or you can decide not to arm the system
Good point! The alarm should trigger the siren or not arm if a sensor is on.
My understanding of on_state
is that it is a combination of on_press
and on_release
, so I’m not sure how it helps here. We also want to catch the case where a door is open during arming and stays open for example.
Edit: This case will not fire any of the on
events I think.
where can I find your updated code, with the restoration of the previous state?
sorry being Italian with the help of a translator, I did not understand your request well.
on_state combined with an “if” if the binary sensor is on then the door or window is open, instead of arming it triggers or disarms.
No worries, I think I understand now. Thanks for the additional info.
The code is in the gist in the first post, and again here for convenience:
I don’t see any changes compared to the post initial code to maintain the state in case of reboot.
This is what I am using
#allarme esphome - home assistant
#ws2812b
#led 1 sensore 1
#led 2 sensore 2
#led 3 stato connessione api con home assistant
#led 4 stato connessione wifi
#led 5 stato allarme armato/disarmato
#led 6 stato allarme triggered
#GPIO4 D2 display 16x2 sda
#GPIO5 D1 display 16x2 scl
#GPIO2 D4 led onboard stato esp
#GPIO16 D0 pir/zona 1
#GPIO13 D7 pir/zona 2
#GPIO15 D8 buzzer
#GPIO12 D6 sirena
#GPIO0 D3 ws2812b led status
substitutions:
alarm_code: "xxxxxxx"
disarmed_id: "0"
pending_id: "1"
triggered_id: "2"
armed_home_id: "3"
esphome:
name: antifurto_nodemcu
platform: ESP8266
board: nodemcu
# restore state
# if trigger sequence reset to armed for now.
# should it retrigger sequence?
on_boot:
then:
- lambda: |-
if (id(state_int) == ${disarmed_id}) {
id(alarm_condition).publish_state("disarmed");
} else if (id(state_int) == ${pending_id}){
id(alarm_condition).publish_state("armed_home");
id(state_int) = ${armed_home_id};
} else if (id(state_int) == ${triggered_id}){
id(alarm_condition).publish_state("armed_home");
id(state_int) = ${armed_home_id};
} else if (id(state_int) == ${armed_home_id}){
id(alarm_condition).publish_state("armed_home");
} else{
id(alarm_condition).publish_state("disarmed");
id(state_int) = ${disarmed_id};
}
- light.turn_on:
id: my_light
- delay: 3s
- light.addressable_set:
id: my_light
range_from: 0
range_to: 5
red: 0%
green: 0%
blue: 0%
- script.execute: boot_state
wifi:
ssid: "xxxxxx"
password: "xxxxxx"
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Antifurto Nodemcu"
password: "xxxxxx"
# Example configuration entry
web_server:
port: 80
captive_portal:
# Enable logging
logger:
# Enable Home Assistant API
api:
# add arm and disarm services to ha
services:
- service: arm_home
variables:
code: string
then:
- if:
condition:
lambda: "return code == \"${alarm_code}\";"
then:
- script.execute: alarm_arm
- if:
condition:
lambda: "return id(pir_cucina).state;"
then:
- script.execute: trigger_alarm
- if:
condition:
lambda: "return id(pir_corridoio).state;"
then:
- script.execute: trigger_alarm
- service: disarm
variables:
code: string
then:
- if:
condition:
lambda: "return code == \"${alarm_code}\";"
then:
- script.execute: alarm_disarm
ota:
# global for state restore
i2c:
sda: GPIO4
scl: GPIO5
display:
- platform: lcd_pcf8574
dimensions: 16x2
address: 0x27
id: lcd
lambda: |-
it.strftime( "%H:%M %d.%m.%Y", id(my_time).now());
if (id(state_int) == ${disarmed_id}) {
it.print(0, 1,"Stato disarmato");
}
if (id(state_int) == ${armed_home_id}) {
it.print(0, 1,"Stato armato");
}
if (id(state_int) == ${pending_id}) {
it.print(0, 1,"Stato innescato");
}
if (id(state_int) == ${triggered_id}) {
it.print(0, 1,"Stato in allarme");
}
time:
- platform: sntp
id: my_time
globals:
- id: state_int
type: int
restore_value: yes
initial_value: '0'
# Example configuration entrlight:
light:
- platform: fastled_clockless
chipset: WS2811
pin: D3
num_leds: 6
internal: true
rgb_order: GRB
id: my_light
name: "FastLED WS2811 Light"
sensor:
- platform: uptime
name: Uptime Sensor
- platform: wifi_signal
name: "WiFi Signal"
update_interval: 60s
status_led:
pin: GPIO2
binary_sensor:
- platform: status
name: "ESP ANTIFURTO"
- platform: gpio
pin:
number: D0
mode: INPUT_PULLUP
name: "Pir corridoio"
id: pir_corridoio
device_class: door
on_state:
then:
- if:
condition:
binary_sensor.is_on: pir_corridoio
then:
- script.execute: trigger_alarm
- if:
condition:
- and:
binary_sensor.is_on: pir_corridoio
- lambda: |-
return id(state_int) == ${disarmed_id};
then:
- light.addressable_set:
id: my_light
range_from: 0
range_to: 0
red: 50%
green: 50%
blue: 0%
- if:
condition:
- and:
binary_sensor.is_off: pir_corridoio
- lambda: |-
return id(state_int) == ${disarmed_id};
then:
- light.addressable_set:
id: my_light
range_from: 0
range_to: 0
red: 0%
green: 0%
blue: 0%
- if:
condition:
- and:
binary_sensor.is_on: pir_corridoio
- lambda: |-
return id(state_int) == ${pending_id};
then:
- light.addressable_set:
id: my_light
range_from: 0
range_to: 0
red: 50%
green: 50%
blue: 0%
- platform: gpio
pin:
number: D7
mode: INPUT_PULLUP
name: "Pir cucina"
id: pir_cucina
device_class: door
on_state:
then:
- if:
condition:
binary_sensor.is_on: pir_cucina
then:
- script.execute: trigger_alarm
- if:
condition:
- and:
binary_sensor.is_on: pir_cucina
- lambda: |-
return id(state_int) == ${disarmed_id};
then:
- light.addressable_set:
id: my_light
range_from: 1
range_to: 1
red: 50%
green: 50%
blue: 0%
- if:
condition:
- and:
binary_sensor.is_off: pir_cucina
- lambda: |-
return id(state_int) == ${disarmed_id};
then:
- light.addressable_set:
id: my_light
range_from: 1
range_to: 1
red: 0%
green: 0%
blue: 0%
- if:
condition:
- and:
binary_sensor.is_on: pir_cucina
- lambda: |-
return id(state_int) == ${pending_id};
then:
- light.addressable_set:
id: my_light
range_from: 1
range_to: 1
red: 50%
green: 50%
blue: 0%
interval:
- interval: 10s
then:
- if:
condition:
wifi.connected:
then:
- light.addressable_set:
id: my_light
range_from: 3
range_to: 3
red: 0%
green: 0%
blue: 100%
else:
- light.addressable_set:
id: my_light
range_from: 3
range_to: 3
red: 0%
green: 0%
blue: 0%
- if:
condition:
api.connected:
then:
- light.addressable_set:
id: my_light
range_from: 2
range_to: 2
red: 0%
green: 0%
blue: 100%
else:
- light.addressable_set:
id: my_light
range_from: 2
range_to: 2
red: 0%
green: 0%
blue: 0%
output:
- platform: gpio
pin: D8
id: output_buzzer_front_door
- platform: gpio
pin: D6
id: output_sirena
# warning chirp when pending
switch:
- platform: template
turn_on_action:
- switch.template.publish:
id: buzzer_front_door
state: ON
- while:
condition:
switch.is_on: buzzer_front_door
then:
- light.addressable_set:
id: my_light
range_from: 5
range_to: 5
red: 100%
green: 0%
blue: 0%
- output.turn_on: output_buzzer_front_door
- delay: 10ms
- light.addressable_set:
id: my_light
range_from: 5
range_to: 5
red: 0%
green: 0%
blue: 0%
- output.turn_off: output_buzzer_front_door
- delay: 990ms
turn_off_action:
- switch.template.publish:
id: buzzer_front_door
state: OFF
- output.turn_off: output_buzzer_front_door
id: buzzer_front_door
# holds alarm condition for arm/disarm/pending/triggered
text_sensor:
- platform: template
name: "Alarm Condition"
id: "alarm_condition"
script:
# when arming and disarming, turn off all sirens and buzzers
# also, cancel any trigger sequence
- id: alarm_arm
then:
- text_sensor.template.publish:
id: alarm_condition
state: "armed_home"
- script.stop: trigger_alarm_execute
- switch.turn_off: buzzer_front_door
- output.turn_off: output_sirena
- light.addressable_set:
id: my_light
range_from: 5
range_to: 5
red: 0%
green: 0%
blue: 0%
- light.addressable_set:
id: my_light
range_from: 4
range_to: 4
red: 100%
green: 0%
blue: 0%
- lambda: |-
id(state_int) = ${armed_home_id};
- id: alarm_disarm
then:
- text_sensor.template.publish:
id: alarm_condition
state: "disarmed"
- script.stop: trigger_alarm_execute
- switch.turn_off: buzzer_front_door
- output.turn_off: output_sirena
- lambda: |-
id(state_int) = ${disarmed_id};
- light.addressable_set:
id: my_light
range_from: 5
range_to: 5
red: 0%
green: 0%
blue: 0%
- light.addressable_set:
id: my_light
range_from: 4
range_to: 4
red: 0%
green: 100%
blue: 0%
# triggering sequence
- id: trigger_alarm_execute
then:
- text_sensor.template.publish:
id: alarm_condition
state: "pending"
- switch.turn_on: buzzer_front_door
- lambda: |-
id(state_int) = ${pending_id};
- delay: 30s
- text_sensor.template.publish:
id: alarm_condition
state: "triggered"
- switch.turn_off: buzzer_front_door
- output.turn_on: output_sirena
- light.addressable_set:
id: my_light
range_from: 5
range_to: 5
red: 100%
green: 0%
blue: 0%
- lambda: |-
id(state_int) = ${triggered_id};
- delay: 3600s
# TODO: turn on siren
# only execute triggering if armed and not already running
- id: trigger_alarm
then:
if:
condition:
and:
- not:
script.is_running: trigger_alarm_execute
- lambda: |-
return id(state_int) == ${armed_home_id};
then:
script.execute: trigger_alarm_execute
- id: boot_state
then:
- if:
condition:
or:
- lambda: |-
return id(state_int) == ${pending_id};
- lambda: |-
return id(state_int) == ${triggered_id};
- lambda: |-
return id(state_int) == ${armed_home_id};
then:
- light.addressable_set:
id: my_light
range_from: 4
range_to: 4
red: 100%
green: 0%
blue: 0%
else:
- light.addressable_set:
id: my_light
range_from: 4
range_to: 4
red: 0%
green: 100%
blue: 0%