Skip to content

"Making Home Assistant's Presence Detection not so Binary" Remix

Nate edited this page Nov 18, 2020 · 2 revisions

This is a pyscript implementation of this Node-RED flow for presence detection: Making Home Assistant's Presence Detection not so Binary (Node-RED version).

Only prerequisite is that you create an input_select in HA called input_select.{person_name}_home_status (i.e. input_select.tony_stark_home_status) to hold onto the person's status.

configuration.yaml

pyscript:
  apps:
    person_tracker:
      - person: person.tony_stark

pyscript/apps/person_tracker.py

registered_triggers = []

def loadApp(app_name, factory):
    if 'apps' not in pyscript.config:
        return
    
    if app_name not in pyscript.config['apps']:
        return

    for app in pyscript.config['apps'][app_name]:
        factory(app)


@time_trigger('startup')
def personTrackerStartup():
    loadApp('person_tracker', buildPersonTracker)


def buildPersonTracker(config):
    global registered_triggers
    personID = config['person']
    personName = personID.split('.')[1]
    inputID = f'input_select.{personName}_home_status'

    @task_unique(f'{personName}_tracker')
    @state_trigger(personID)
    def tracker(value=None):
        nonlocal personID
        personState = state.get(inputID)

        if value == 'home':
            input_select.select_option(
                entity_id=inputID, option='Just Arrived')

            minutes = 10
            waitForHome = task.wait_until(
                timeout=minutes*60,
                state_trigger=(f'{personID} != "home"'),
                state_check_now=False
            )

            if waitForHome['trigger_type'] == 'timeout':
                # x mins elapsed without leaving home
                input_select.select_option(
                    entity_id=inputID, option='Home')
        else:
            if personState not in ['Just Left', 'Away', 'Extended Away']:
                input_select.select_option(
                    entity_id=inputID, option='Just Left')

                minutes = 15
                waitForAway = task.wait_until(
                    timeout=minutes*60,
                    state_trigger=(f'{personID} == "home"'),
                    state_check_now=False
                )

                if waitForAway['trigger_type'] == 'timeout':
                    # x mins elapsed without coming home
                    input_select.select_option(
                        entity_id=inputID, option='Away')

                    # wait longer for 'Extended Away'
                    hours = 2
                    waitForExtendedAway = task.wait_until(
                        timeout=hours*3600,
                        state_trigger=(f'{personID} == "home"'),
                        state_check_now=True
                    )

                    if waitForExtendedAway['trigger_type'] == 'timeout':
                        # x hrs elapsed without coming home
                        input_select.select_option(
                            entity_id=inputID, option='Extended Away')

    # register to global scope
    registered_triggers.append([tracker])