Skip to content

Commit

Permalink
Merge pull request #21 from ad-ha/ad-ha-kc-0.1.4-beta-rc4
Browse files Browse the repository at this point in the history
0.1.4-beta-rc4
  • Loading branch information
ad-ha authored Feb 1, 2025
2 parents da24b6e + f4716b2 commit 330ca72
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 12 deletions.
77 changes: 66 additions & 11 deletions custom_components/kidschores/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,51 @@ def __init__(
self.storage_manager = storage_manager
self._data: dict[str, Any] = {}

# -------------------------------------------------------------------------------------
# Migrate Data and Converters
# -------------------------------------------------------------------------------------

def _migrate_datetime(self, dt_str: str) -> str:
"""Convert a datetime string to a UTC-aware ISO string.
If the string is naive (lacking tzinfo), assume it represents local time.
"""
try:
# Try to parse using Home Assistant’s utility first:
dt_obj = dt_util.parse_datetime(dt_str)
if dt_obj is None:
# Fallback using fromisoformat
dt_obj = datetime.fromisoformat(dt_str)
# If naive, assume local time and make it aware:
if dt_obj.tzinfo is None:
dt_obj = dt_util.make_aware(dt_obj, dt_util.get_local_time_zone())
# Convert to UTC
dt_obj_utc = dt_util.as_utc(dt_obj)
return dt_obj_utc.isoformat()
except Exception as err:
LOGGER.warning("Error migrating datetime '%s': %s", dt_str, err)
return dt_str

def _migrate_stored_datetimes(self):
"""Walk through stored data and convert known datetime fields to UTC-aware ISO strings."""
# For each chore, migrate due_date, last_completed, and last_claimed
for chore in self._data.get(DATA_CHORES, {}).values():
if chore.get("due_date"):
chore["due_date"] = self._migrate_datetime(chore["due_date"])
if chore.get("last_completed"):
chore["last_completed"] = self._migrate_datetime(
chore["last_completed"]
)
if chore.get("last_claimed"):
chore["last_claimed"] = self._migrate_datetime(chore["last_claimed"])
# Also, migrate timestamps in pending approvals
for approval in self._data.get(DATA_PENDING_CHORE_APPROVALS, []):
if approval.get("timestamp"):
approval["timestamp"] = self._migrate_datetime(approval["timestamp"])
for approval in self._data.get(DATA_PENDING_REWARD_APPROVALS, []):
if approval.get("timestamp"):
approval["timestamp"] = self._migrate_datetime(approval["timestamp"])

# -------------------------------------------------------------------------------------
# Periodic + First Refresh
# -------------------------------------------------------------------------------------
Expand Down Expand Up @@ -121,6 +166,10 @@ async def async_config_entry_first_refresh(self):
stored_data = self.storage_manager.get_data()
if stored_data:
self._data = stored_data

# Migrate any datetime fields in stored data to UTC-aware strings
self._migrate_stored_datetimes()

else:
self._data = {
DATA_KIDS: {},
Expand Down Expand Up @@ -511,21 +560,27 @@ def _update_badge(self, badge_id: str, badge_data: dict[str, Any]):
badge_info = self._data[DATA_BADGES][badge_id]
badge_info["name"] = badge_data.get("name", badge_info["name"])
badge_info["threshold_type"] = badge_data.get(
"threshold_type", badge_info["threshold_type"]
"threshold_type",
badge_info.get("threshold_type", BADGE_THRESHOLD_TYPE_POINTS),
)
badge_info["threshold_value"] = badge_data.get(
"threshold_value", badge_info["threshold_value"]
"threshold_value",
badge_info.get("threshold_value", DEFAULT_BADGE_THRESHOLD),
)
badge_info["chore_count_type"] = badge_data.get(
"chore_count_type", badge_info["chore_count_type"]
"chore_count_type", badge_info.get("chore_count_type", FREQUENCY_NONE)
)
badge_info["points_multiplier"] = badge_data.get(
"points_multiplier", badge_info["points_multiplier"]
"points_multiplier",
badge_info.get("points_multiplier", DEFAULT_POINTS_MULTIPLIER),
)
badge_info["icon"] = badge_data.get(
"icon", badge_info.get("icon", DEFAULT_ICON)
)
badge_info["icon"] = badge_data.get("icon", badge_info["icon"])
badge_info["description"] = badge_data.get(
"description", badge_info["description"]
"description", badge_info.get("description", "")
)

LOGGER.debug("Updated badge '%s' with ID: %s", badge_info["name"], badge_id)

# -- Rewards
Expand Down Expand Up @@ -704,7 +759,7 @@ def claim_chore(self, kid_id: str, chore_id: str, user_name: str):
if chore_id not in kid_info.get("claimed_chores", []):
kid_info.setdefault("claimed_chores", []).append(chore_id)

chore_info["last_claimed"] = datetime.now().isoformat()
chore_info["last_claimed"] = dt_util.utcnow().isoformat()

# increment chore_claims
if chore_id in kid_info["chore_claims"]:
Expand All @@ -717,7 +772,7 @@ def claim_chore(self, kid_id: str, chore_id: str, user_name: str):
{
"kid_id": kid_id,
"chore_id": chore_id,
"timestamp": datetime.now().isoformat(),
"timestamp": dt_util.utcnow().isoformat(),
}
)

Expand Down Expand Up @@ -812,7 +867,7 @@ def approve_chore(
kid_info["completed_chores_monthly"] += 1
kid_info["completed_chores_total"] += 1

chore_info["last_completed"] = datetime.now().isoformat()
chore_info["last_completed"] = dt_util.utcnow().isoformat()

# remove from pending approvals
self._data[DATA_PENDING_CHORE_APPROVALS] = [
Expand Down Expand Up @@ -1007,7 +1062,7 @@ def redeem_reward(self, parent_name: str, kid_id: str, reward_id: str):
{
"kid_id": kid_id,
"reward_id": reward_id,
"timestamp": datetime.now().isoformat(),
"timestamp": dt_util.utcnow().isoformat(),
}
)

Expand Down Expand Up @@ -1529,7 +1584,7 @@ def _handle_recurring_chore(self, chore_info: dict[str, Any]):
if freq == FREQUENCY_NONE:
return
# daily, weekly, monthly logic if chore lacks a due_date
now = datetime.now()
now = dt_util.utcnow()
if freq == FREQUENCY_DAILY:
next_due = now + timedelta(days=1)
elif freq == FREQUENCY_WEEKLY:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/kidschores/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "local_polling",
"issue_tracker": "https://github.com/ad-ha/kidschores-ha/issues",
"requirements": [],
"version": "0.1.3-beta-rc4"
"version": "0.1.4-beta-rc4"
}

0 comments on commit 330ca72

Please sign in to comment.