Schlage FE599 Lock - Last Action Detection Finally Working

The Schlage FE599 lock does not have built in detection of “last actions”. I see three possible cases:

  1. Lock/Unlock by User Code Entry
  2. Lock/Unlock by Home Assistant (through automations or through frontend)
  3. Lock/Unlock by Manual Interaction (lock/unlock without entering door code is possible from inside the house)

This seems to be working, even when the same code is entered multiple times in a row.

This trigger is not specific to the FE599 lock, it will be triggered by any lock. I assume it is possible to make this specific to a single lock.

########################################################################################################################
##### Garage Door Lock Stuff:                                                                                     ######
#####   Schlage FE599 Specific --> deduce states, must have 5 seconds between operations to be valid              ######
########################################################################################################################
  ### Used to determine when Home Assistant Locks/Unlocks Garage Door
      #### Triggered off of Home Assistant Lock Service Calls.  NOT ENTITY ID SPECIFIC!  ## ToDo / Needs Work
      #### Creates a 5 second long pulse to indicate lock/unlock state changes are the result
      ####   of Home Assistant and not the user pressing the INDOOR Lock/Unlock Buttons
  - id: lock_service_detector
    alias: "Lock Service Detector"
    trigger:
      - platform: event
        event_type: call_service
        event_data:
          domain: lock
    action:
      - service: homeassistant.turn_on
        entity_id: input_boolean.lock_service
      - delay: '00:00:05'
      - service: homeassistant.turn_off
        entity_id: input_boolean.lock_service

Detection of User Code Entry:

  - platform: template
    sensors:
      garage_door_code_entered:
        entity_id:
          - sensor.garage_door_keypad_lock_alarm_level
          ### ToDo: Should only need a one-shot update to clear after being set (date_time was used, but there can be up to a mintute between subsequent detections)
          - input_boolean.clock_5sec
        value_template: >-
          {% if ((as_timestamp(now()) - as_timestamp(states.sensor.garage_door_keypad_lock_alarm_level.last_changed)) < 1) %}
            {{ states.sensor.garage_door_keypad_lock_alarm_level.state }}
          {% else %}
            0
          {% endif %}
      garage_door_last_action:
        friendly_name: 'Garage Entry Door Last Action'
        entity_id: 
          - zwave.garage_door_keypad
          - input_select.lock_hass_status_for_garage_door_lock
          - input_boolean.clock_5sec
          #### ToDo: Can the below triggers be used instead of clock_5sec?
          # - sensor.garage_door_code_entered
          # - lock.garage_door_keypad
        value_template: > 
            {% if states.sensor.garage_door_code_entered.state != "0" %}
              {% if is_state('sensor.garage_door_keypad_lock_alarm_type', '16') %}
                {% if is_state('sensor.garage_door_keypad_lock_alarm_level', '3') %}
                    Unlocked: Scuteri Family Code
                {% elif is_state('sensor.garage_door_keypad_lock_alarm_level', '4') %}
                    Unlocked: Cleaning Service Code
                {% else %}
                  Unlocked: User Code {{states('sensor.garage_door_keypad_lock_alarm_level')}}
                {% endif %}
              {% else %}
                Who Knows
              {% endif %}
            {% elif is_state('input_select.lock_hass_status_for_garage_door_lock', 'Door Code Used') %}
             {% if is_state('lock.garage_door_keypad', 'locked') %}
                Auto Re-Locked
              {% else %}
                Unlocked by Ghost
              {% endif %}
            {% else %}
              {{ states.input_select.lock_hass_status_for_garage_door_lock.state }}
            {% endif %}
  ## When door is locked and set to Auto Re-Lock, the lock state doesn't toggle when user codes are entered
      ## The "sensor.garage_door_code_entered" entity exploits this behavior
  - id: garage_door_code_used
    alias: "Garage Door Code Used"
    trigger:
      - platform: state
        entity_id: sensor.garage_door_code_entered
        from: '0'
    condition:
      condition: state
      entity_id: input_boolean.ha_initialized
      state: 'on'
    action:
      - service: input_select.select_option
        data:
          entity_id: input_select.lock_hass_status_for_garage_door_lock
          option: Door Code Used
      - service: input_select.select_option
        data_template:
          entity_id: input_select.garage_lock_last_code
          option: > 
            {% if is_state('sensor.garage_door_keypad_lock_alarm_level', '4') %}
              Cleaner Code
            {% elif is_state('sensor.garage_door_keypad_lock_alarm_level', '3') %}
              Scuteri Family Code
            {% else %}
              Other
            {% endif %}
      ## Avoid duplicate text messages for cleaner code  --> Want to know if cleaner leaves and comes back
      - condition: template
        value_template: >
          {% if is_state("input_select.garage_lock_last_code", "Cleaner Code") and is_state("timer.cleaner_code_text_sent", "active") %}
            false
          {% else %}
            true
          {% endif %}
      # Family Code and Cleaner Code use codes 3 and 4
      - service: notify.text_jeremy_nichole
        data_template:
          message: > 
            {% if is_state('sensor.garage_door_keypad_lock_alarm_level', '3') %}
              Family Code Used To Unlock Garage Door
            {% elif is_state('sensor.garage_door_keypad_lock_alarm_level', '4') %}
              Cleaning Service Code Used To Unlock Garage Door
            {% else %}
              User Code {{ states.sensor.garage_door_keypad_lock_alarm_level.state }} Used To Unlock Garage Door
            {% endif %}
      - condition: state
        entity_id: input_select.garage_lock_last_code
        state: 'Cleaner Code'
      - service: timer.start
        entity_id: timer.cleaner_code_text_sent

Lock/Unlock by Home Assistant or Manual Interaction

  ## Triggered by change state of lock entity (NOT User Code Entries since they don't change the entity state)
  - id: garage_lock_change_detection
    alias: "Garage Door Lock Change Detection"
    # Any change in lock state, lock or unlock
    trigger:
      - platform: state
        entity_id: lock.garage_door_keypad
    # Ensure user code has not been entered (Could be remote command from Home Assistant or Manual Lock/Unlock Button User Interaction)
    condition:
      condition: state
      entity_id: sensor.garage_door_code_entered
      state: '0'
    # input_select is used for tracking
    action:
      - service: input_select.select_option
        data_template:
          entity_id: input_select.lock_hass_status_for_garage_door_lock
          option: >
            {% if is_state("input_boolean.lock_service", "on") %}
              {% if is_state("lock.garage_door_keypad", "locked") %}
                Locked by Home Assistant
              {% else %}
                Unlocked by Home Assistant
              {% endif %}
            {% else %}
              {% if is_state("lock.garage_door_keypad", "locked") %}
                Manually Locked
              {% else %}
                Manually Unlocked
              {% endif %}
            {% endif %}
input_select:
  lock_hass_status_for_garage_door_lock:
    name: Garage Door Lock HASS Status
    icon: mdi:lock
    options:
      - Locked by Home Assistant
      - Unlocked by Home Assistant
      - Manually Locked
      - Manually Unlocked
      - Door Code Used
      - Locked
      - Unlocked
      - Other

Thanks, this has given me a lot to think about and try to implement. I did come up with a different solution to the same code entered multiple times in a row.

I came across this post for changing the state of an entity, and implemented the python script written by @rodpayne. Then I created the following two automations - basically making alarm_level and alarm_type momentary rather than fixed.

- alias: Front Door Alarm Level
  hide_entity: false
  initial_state: true
  trigger:
    platform: state
    entity_id: sensor.schlage_front_door_lock_alarm_level
    from: '0'
  action:
    - delay: '00:00:05'
    - service: python_script.set_state
      data_template:
        entity_id: sensor.schlage_front_door_lock_alarm_level
        state: '0'

- alias: Front Door Alarm Type
  hide_entity: false
  initial_state: true
  trigger:
    platform: state
    entity_id: sensor.schlage_front_door_lock_alarm_type
    from: '0'
  action:
    - delay: '00:00:05'
    - service: python_script.set_state
      data_template:
        entity_id: sensor.schlage_front_door_lock_alarm_type
        state: '0'
1 Like

Thanks, this is exactly what I needed! One step to note (obvious now that I think about it) is I had to go into the developer tools section and reset the state of the alarm levels to 0 before this automation started working.