Skip to content

Commit

Permalink
Feat: Add code generators
Browse files Browse the repository at this point in the history
Teach the event sensor 3 ways to generate door codes.

The default (and fall back) is check-in/out day based where the check-in
day and check-out day are combined to create a 4 digit code

A random 4 digit code seeded from the event description

Finally, the last 4 digits of a phone number - only works if the
description has the text 'Last 4 Digits' followed quickly by a 4 digit
number. This is by far the most stable, but only works if the conditions
are correct

Issue: #38
Signed-off-by: Andrew Grimberg <tykeal@bardicgrove.org>
  • Loading branch information
tykeal committed Mar 20, 2022
1 parent 7a7d9f3 commit bbdf601
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 1 deletion.
4 changes: 4 additions & 0 deletions custom_components/rental_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@

from .const import CONF_CHECKIN
from .const import CONF_CHECKOUT
from .const import CONF_CODE_GENERATION
from .const import CONF_DAYS
from .const import CONF_EVENT_PREFIX
from .const import CONF_IGNORE_NON_RESERVED
from .const import CONF_MAX_EVENTS
from .const import CONF_REFRESH_FREQUENCY
from .const import CONF_TIMEZONE
from .const import DEFAULT_CODE_GENERATION
from .const import DEFAULT_REFRESH_FREQUENCY
from .const import DOMAIN
from .const import PLATFORMS
Expand Down Expand Up @@ -146,6 +148,7 @@ def __init__(self, hass, config):
self.ignore_non_reserved = config.get(CONF_IGNORE_NON_RESERVED)
self.verify_ssl = config.get(CONF_VERIFY_SSL)
self.calendar = []
self.code_generator = config.get(CONF_CODE_GENERATION, DEFAULT_CODE_GENERATION)
self.event = None
self.all_day = False

Expand Down Expand Up @@ -214,6 +217,7 @@ def update_config(self, config):
self.checkout = cv.time(config.get(CONF_CHECKOUT))
self.max_events = config.get(CONF_MAX_EVENTS)
self.days = config.get(CONF_DAYS)
self.code_generator = config.get(CONF_CODE_GENERATION, DEFAULT_CODE_GENERATION)
# Early versions did not have this variable, as such it may not be
# set, this should guard against issues until we're certain
# we can remove this guard.
Expand Down
63 changes: 62 additions & 1 deletion custom_components/rental_control/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Creating sensors for upcoming events."""
import logging
import random
import re
from datetime import datetime
from datetime import timedelta

Expand Down Expand Up @@ -36,7 +38,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
sensors = []
for eventnumber in range(max_events):
sensors.append(
ICalSensor(hass, rental_control_events, DOMAIN + " " + name, eventnumber)
ICalSensor(
hass,
rental_control_events,
DOMAIN + " " + name,
eventnumber,
)
)

async_add_entities(sensors)
Expand Down Expand Up @@ -77,9 +84,60 @@ def __init__(self, hass, rental_control_events, sensor_name, event_number):
"start": None,
"end": None,
"eta": None,
"slot_code": None,
}
self._state = summary
self._is_available = None
self._code_generator = rental_control_events.code_generator

def _generate_door_code(self) -> str:
"""Generate a door code based upon the selected type."""

generator = self._code_generator

# If there is no event description force date_based generation
# This is because VRBO does not appear to provide any descriptions in
# their calendar entries!
# This also gets around Unavailable and Blocked entries that do not
# have a description either
if self._event_attributes["description"] is None:
generator = "date_based"

# AirBnB provides the last 4 digits of the guest's registered phone
#
# VRBO does not appear to provide any phone numbers
#
# Guesty provides last 4 + either a full number or all but last digit
# for VRBO listings and doesn't appear to provide anything for AirBnB
# listings, or if it does provide them, my example Guesty calendar doesn't
# have any new enough to have the data
#
# TripAdvisor does not appear to provide any phone number data

ret = None

if generator == "last_four":
p = re.compile("\\(Last 4 Digits\\):\\s+(\\d{4})")
last_four = p.findall(self._event_attributes["description"])[0]
ret = last_four
elif generator == "static_random":
# If the description changes this will most likely change the code
random.seed(self._event_attributes["description"])
ret = str(random.randrange(1, 9999, 4)).zfill(4)

if ret is None:
# Generate code based on checkin/out days
#
# This generator will have a side effect of changing the code
# if the start or end dates shift!
#
# This is the default and fall back generator if no other
# generator produced a code
start_day = self._event_attributes["start"].strftime("%d")
end_day = self._event_attributes["end"].strftime("%d")
return f"{start_day}{end_day}"
else:
return ret

@property
def entity_id(self):
Expand Down Expand Up @@ -117,6 +175,7 @@ async def async_update(self):

await self.rental_control_events.update()

self._code_generator = self.rental_control_events.code_generator
event_list = self.rental_control_events.calendar
if event_list and (self._event_number < len(event_list)):
val = event_list[self._event_number]
Expand Down Expand Up @@ -144,6 +203,7 @@ async def async_update(self):
self._state = f"{name} - {start.strftime('%-d %B %Y')}"
if not val.get("all_day"):
self._state += f" {start.strftime('%H:%M')}"
self._event_attributes["slot_code"] = self._generate_door_code()
else:
# No reservations
_LOGGER.debug(
Expand All @@ -162,5 +222,6 @@ async def async_update(self):
"start": None,
"end": None,
"eta": None,
"slot_code": None,
}
self._state = summary

0 comments on commit bbdf601

Please sign in to comment.