Esphome alarm system

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%

The on_boot section does the restore state logic. On an esp8266 you have to enable restoring from flash or it wonā€™t survive a power cycle. But, beware! My understanding is that this can wear the flash memory. For an alarm system there shouldnā€™t be too many writes too flash, but I want to highlight that.

Or maybe I dontā€™ understand your question. Your code looks OK from quick inspection for restoring from a normal reboot (I think this can be caused by lack of wifi connection sometimes depending on settings).

Also I like your usage of lights to indicate different states etc and a display. This is on my todo list.

Are you telling me I have to enable this? esp8266_restore_from_flash ā€¦ My code is working right now. You have dealt with the speech of the restoration and I understood that with this code that I published the restoration occurs by reading the text sensor from the home assistant, and if the server is turned off it cannot be restored. Or am I wrong?

The code you have posted resets state based on the last state on the esp unit itself. It is my understanding that your version wonā€™t work across power cycles without that esp8266_restore_from_flash option enabled. I have only tested esp32 which only writes to flash as part of esphome as far as I know.

Sorry but I donā€™t want to get confused. restore_value: equals esp8266_restore_from_flash

Are you telling me that HA restores based on the global esphome? So in case of power failure and then reboot you how do you restore the esp to the initial state? Can you confirm that the restore_value has 100,000 write cycles?

I use the template alarm control panel in home assistant, it purely follows whatever the esp unit state is. There is no logic in home assistant other than reading the state and sending arm and disarm commands.

There esp unit restores based on the global variable written to itā€™s own flash memory. I donā€™t know about the number of cycles and the way it works isnā€™t super straightforward when I looked into it.

I studied your code before putting it into practice. Its logic is based on esp, and thatā€™s clear and itā€™s a great choice. I donā€™t understand if the restore_value: yes, inserted in the global variable allows esp to restore the previous state in case of loss of power

This needs to be enabled on my understanding to restore state after power cycle.

We seem to be responding to older posts before a new one comes in, I think this hasnā€™t helped communications. The restore_value: yes will restore the value of the global variable. My understanding is that this happens from RTC memory on esp8266 in esphome by default. This kind of memory does not store data when power is lost. The esp8266_restore_from_flash makes this behavior happen in the flash memory which does survive power cycle

I donā€™t need the esp8266 option because Iā€™m using esp32.

I read some documentation. Youā€™re right, the function is the one you indicated.

I purchased an esp32 and am waiting for it to arrive. How does this mode work on esp32?

There is no option in esphome for RTC vs flash in esp32 as far as I know. It only writes to flash. This isnā€™t in the documentation, but I was told this in the esphome discord.

Hey, awesome project!
Iā€™ll modify this to work with my current boardā€¦ and use it to arm/disarm itā€¦

In HASS, I canā€™t get it to arm/disarmā€¦ ie type code - nothing happensā€¦
canā€™t seem to figure out why (very new to HASS)ā€¦

any tips or tricks?

This is a complex set of code on the ESP unit and in home assistant. I would recommend first making sure you can arm and disarm the system using services from home assistant. To debug this, you need to use the logs on the ESP unit itself in addition to the logs in home assistant.