Skip to content

Commit

Permalink
Code cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
rodpayne committed Jun 7, 2024
1 parent 906fe69 commit 7b78190
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 87 deletions.
19 changes: 16 additions & 3 deletions custom_components/person_location/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from homeassistant.components.mobile_app.const import ATTR_VERTICAL_ACCURACY
from homeassistant.components.waze_travel_time.const \
import REGIONS as WAZE_REGIONS
from homeassistant.components.zone.const \
import DOMAIN as ZONE_DOMAIN
from homeassistant.const import (
ATTR_ATTRIBUTION,
ATTR_FRIENDLY_NAME,
Expand All @@ -17,6 +19,10 @@
ATTR_LATITUDE,
ATTR_LONGITUDE,
ATTR_UNIT_OF_MEASUREMENT,
STATE_ON,
STATE_OFF,
STATE_HOME,
STATE_NOT_HOME,
STATE_UNKNOWN,
)
from homeassistant.util.yaml.objects import (
Expand All @@ -29,7 +35,7 @@
API_STATE_OBJECT = DOMAIN + "." + DOMAIN + "_integration"
INTEGRATION_NAME = "Person Location"
ISSUE_URL = "https://github.com/rodpayne/home-assistant_person_location/issues"
VERSION = "2024.06.04"
VERSION = "2024.06.07"

# Constants:
METERS_PER_KM = 1000
Expand All @@ -51,8 +57,15 @@
ATTR_DRIVING_MILES = "driving_miles"
ATTR_DRIVING_MINUTES = "driving_minutes"
ATTR_GEOCODED = "geocoded"
ATTR_ICON = "icon"
ATTR_LAST_LOCATED = "last_located"
ATTR_LOCATION_TIME = "location_time"
ATTR_METERS_FROM_HOME = "meters_from_home"
ATTR_MILES_FROM_HOME = "miles_from_home"
ATTR_PERSON_NAME = "person_name"
ATTR_REPORTED_STATE = "reported_state"
ATTR_SOURCE = "source"
ATTR_ZONE = "zone"

# Configuration Version:
CONF_VERSION = 1
Expand Down Expand Up @@ -420,7 +433,7 @@ def __init__(self, _entity_id, _pli):
if targetStateObject is not None:
self.firstTime = False
if (targetStateObject.state.lower().endswith("stationary")) or (
targetStateObject.state == "not_home"
targetStateObject.state == STATE_NOT_HOME
):
self.state = "Away"
else:
Expand Down Expand Up @@ -457,7 +470,7 @@ def __init__(self, _entity_id, _pli):
self.state = "Home"
else:
self.stateHomeAway = "Away"
if self.state == "not_home":
if self.state == STATE_NOT_HOME:
self.state = "Away"

if self.entity_id in self.pli.configuration[CONF_DEVICES]:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/person_location/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"iot_class": "calculated",
"issue_tracker": "https://github.com/rodpayne/home-assistant_person_location/issues",
"requirements": [],
"version": "2024.06.04"
"version": "2024.06.07"
}
85 changes: 48 additions & 37 deletions custom_components/person_location/process_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
ATTR_BREAD_CRUMBS,
ATTR_COMPASS_BEARING,
ATTR_DIRECTION,
ATTR_ICON,
ATTR_LAST_LOCATED,
ATTR_LOCATION_TIME,
ATTR_PERSON_NAME,
ATTR_REPORTED_STATE,
ATTR_SOURCE,
ATTR_ZONE,
CONF_FRIENDLY_NAME_TEMPLATE,
CONF_HOURS_EXTENDED_AWAY,
CONF_MINUTES_JUST_ARRIVED,
Expand All @@ -41,6 +48,7 @@
PERSON_LOCATION_ENTITY,
TARGET_LOCK,
VERSION,
ZONE_DOMAIN,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -107,8 +115,8 @@ def handle_delayed_state_change(
target.attributes[ATTR_DIRECTION] = "home"
elif to_state == "Away":
if pli.configuration[CONF_SHOW_ZONE_WHEN_AWAY]:
reportedZone = target.attributes["zone"]
zoneStateObject = pli.hass.states.get("zone." + reportedZone)
reportedZone = target.attributes[ATTR_ZONE]
zoneStateObject = pli.hass.states.get(ZONE_DOMAIN + "." + reportedZone)
if (zoneStateObject is None
or reportedZone.lower().endswith("stationary")):
_LOGGER.debug(f"Skipping use of zone {reportedZone} for Away state")
Expand Down Expand Up @@ -181,11 +189,11 @@ def handle_process_trigger(call):
Attributes:
- selected attributes from the triggered device tracker
- state: "Just Arrived", "Home", "Just Left", "Away", or "Extended Away"
If CONF_SHOW_ZONE_WHEN_AWAY, then <Zone> is reported instead of "Away".
If CONF_SHOW_ZONE_WHEN_AWAY, then the <Zone> is reported instead of "Away".
- person_name: <personName>
- source: entity_id of the device tracker that triggered the automation
- reported_state: the state reported by device tracker = "Home", "Away", or <zone>
- friendly_name: something like "Rod (i.e. Rod's watch) is at Drew's"
- bread_crumbs: the series of locations that have been seen
- icon: the icon that correspondes with the current zone
- Call rest_command service to update HomeSeer: 'homeseer_<personName>_<state>'
"""
Expand All @@ -194,6 +202,8 @@ def handle_process_trigger(call):
triggerFrom = call.data.get("from_state", "NONE")
triggerTo = call.data.get("to_state", "NONE")

# Validate the input entity:

if entity_id == "NONE":
{
_LOGGER.warning(
Expand Down Expand Up @@ -235,8 +245,8 @@ def handle_process_trigger(call):
)
else:

if "last_located" in trigger.attributes:
last_located = trigger.attributes["last_located"]
if ATTR_LAST_LOCATED in trigger.attributes:
last_located = trigger.attributes[ATTR_LAST_LOCATED]
new_location_time = datetime.strptime(last_located, "%Y-%m-%d %H:%M:%S")
else:
new_location_time = utc2local_naive(
Expand Down Expand Up @@ -273,23 +283,16 @@ def handle_process_trigger(call):

target.this_entity_info["trigger_count"] += 1

if "location_time" in target.attributes:
if ATTR_LOCATION_TIME in target.attributes:
old_location_time = datetime.strptime(
str(target.attributes["location_time"]),
str(target.attributes[ATTR_LOCATION_TIME]),
"%Y-%m-%d %H:%M:%S.%f",
)
else:
old_location_time = utc2local_naive(
target.last_updated
) # HA last_updated is UTC

_LOGGER.debug(
"(%s) new_location_time (from trigger) = %s; old_location_time (from target) = %s",
trigger.entity_id,
new_location_time,
old_location_time,
)

if new_location_time < old_location_time:
_LOGGER.debug(
"(%s) Decision: skip stale update: %s < %s",
Expand Down Expand Up @@ -323,17 +326,17 @@ def handle_process_trigger(call):
)
else:
if (
not ("source" in target.attributes)
not (ATTR_SOURCE in target.attributes)
or target.attributes[ATTR_SOURCE] == trigger.entity_id
or not ("reported_state" in target.attributes)
or target.attributes["source"] == trigger.entity_id
): # same entity as we are following, if any?
saveThisUpdate = True
_LOGGER.debug(
"(%s) Decision: continue following trigger",
trigger.entity_id,
)
elif (
trigger.state == target.attributes["reported_state"]
trigger.state == target.attributes[ATTR_REPORTED_STATE]
): # same status as the one we are following?
if ATTR_VERTICAL_ACCURACY in trigger.attributes:
if (
Expand All @@ -349,7 +352,7 @@ def handle_process_trigger(call):
_LOGGER.debug(
"(%s) Decision: vertical_accuracy is better than %s",
trigger.entity_id,
target.attributes["source"],
target.attributes[ATTR_SOURCE],
)
if (
ATTR_GPS_ACCURACY in trigger.attributes
Expand All @@ -361,7 +364,7 @@ def handle_process_trigger(call):
_LOGGER.debug(
"(%s) Decision: gps_accuracy is better than %s",
trigger.entity_id,
target.attributes["source"],
target.attributes[ATTR_SOURCE],
)
else: # source = router or ping
if triggerTo != triggerFrom: # did tracker change state?
Expand Down Expand Up @@ -444,39 +447,39 @@ def handle_process_trigger(call):
if ATTR_ENTITY_PICTURE in target.attributes:
target.attributes.pop(ATTR_ENTITY_PICTURE)

target.attributes["source"] = trigger.entity_id
target.attributes["reported_state"] = trigger.state
target.attributes["person_name"] = string.capwords(
target.attributes[ATTR_SOURCE] = trigger.entity_id
target.attributes[ATTR_REPORTED_STATE] = trigger.state
target.attributes[ATTR_PERSON_NAME] = string.capwords(
trigger.personName
)
target.attributes["location_time"] \
target.attributes[ATTR_LOCATION_TIME] \
= new_location_time.strftime("%Y-%m-%d %H:%M:%S.%f")

# Determine the zone and the icon to be used:

if "zone" in trigger.attributes:
reportedZone = trigger.attributes["zone"]
if ATTR_ZONE in trigger.attributes:
reportedZone = trigger.attributes[ATTR_ZONE]
else:
reportedZone = (
trigger.state.lower().replace(" ", "_").replace("'", "_")
)
zoneStateObject = pli.hass.states.get("zone." + reportedZone)
zoneStateObject = pli.hass.states.get(ZONE_DOMAIN + "." + reportedZone)
icon = "mdi:help-circle"
if (zoneStateObject is not None
and not reportedZone.lower().endswith("stationary")):
zoneAttributesObject \
= zoneStateObject.attributes.copy()
if "icon" in zoneAttributesObject:
icon = zoneAttributesObject["icon"]
if ATTR_ICON in zoneAttributesObject:
icon = zoneAttributesObject[ATTR_ICON]

target.attributes["icon"] = icon
target.attributes["zone"] = reportedZone
target.attributes[ATTR_ICON] = icon
target.attributes[ATTR_ZONE] = reportedZone

_LOGGER.debug(
"(%s) zone = %s; icon = %s",
trigger.entity_id,
reportedZone,
target.attributes["icon"],
target.attributes[ATTR_ICON],
)

ha_just_started = pli.attributes["startup"]
Expand All @@ -492,15 +495,20 @@ def handle_process_trigger(call):
]

# Set up something like https://philhawthorne.com/making-home-assistants-presence-detection-not-so-binary/
# https://github.com/rodpayne/home-assistant_person_location?tab=readme-ov-file#make-presence-detection-not-so-binary
# If Home Assistant just started, just go with Home or Away as the initial state.

if trigger.stateHomeAway == "Home":
# State is changing to Home.
if (
oldTargetState in ["just left", "none"]
or ha_just_started
or (pli.configuration[
CONF_MINUTES_JUST_ARRIVED] == 0)
):
# Initial setting at startup goes straight to Home.
# Just Left also goes straight back to Home.
# Anything else goes straight to Home if Just Arrived is not an option.
newTargetState = "Home"

target.attributes[
Expand Down Expand Up @@ -530,12 +538,14 @@ def handle_process_trigger(call):
trigger.personName, newTargetState
)
else:
# State is changing to not Home.
if oldTargetState != "away" and (
oldTargetState == "none"
or ha_just_started
or (pli.configuration[
CONF_MINUTES_JUST_LEFT] == 0)
):
# initial setting at startup goes straight to Away
newTargetState = "Away"
if pli.configuration[
CONF_HOURS_EXTENDED_AWAY] != 0:
Expand All @@ -549,13 +559,11 @@ def handle_process_trigger(call):
call_rest_command_service(
trigger.personName, newTargetState
)
elif oldTargetState == "just left":
elif oldTargetState in ["just left", "just arrived"]:
newTargetState = "Just Left"
elif oldTargetState == "away":
newTargetState = "Away"
elif oldTargetState == "extended away":
newTargetState = "Extended Away"
else:
elif oldTargetState == "home":
newTargetState = "Just Left"
change_state_later(
target.entity_id,
Expand All @@ -566,6 +574,9 @@ def handle_process_trigger(call):
call_rest_command_service(
trigger.personName, newTargetState
)
else:
# oldTargetState is either "away" or a Zone
newTargetState = "Away"
if newTargetState == "Away" and pli.configuration[CONF_SHOW_ZONE_WHEN_AWAY]:
# Get the state from the zone friendly_name:
if (zoneStateObject is None
Expand All @@ -589,7 +600,7 @@ def handle_process_trigger(call):

# Call service to "reverse geocode" the location.
# For devices at Home, this will be forced to run
# at startup or on arrival.
# just at startup or on arrival.

force_update = (newTargetState in ["Home",
"Just Arrived"]
Expand Down
Loading

0 comments on commit 7b78190

Please sign in to comment.