Reconcile dev from main
Reconcile dev from main (hotfixes and release commits)
This commit was merged in pull request #141.
This commit is contained in:
@@ -13,14 +13,14 @@ NestQuest helps households coordinate daily life, chore tracking, custody schedu
|
|||||||
1. Ensure [HACS (Home Assistant Community Store)](https://hacs.xyz/) is installed.
|
1. Ensure [HACS (Home Assistant Community Store)](https://hacs.xyz/) is installed.
|
||||||
2. In Home Assistant, open **HACS** > **Integrations**.
|
2. In Home Assistant, open **HACS** > **Integrations**.
|
||||||
3. Select the three-dots menu in the top right and click **Custom repositories**.
|
3. Select the three-dots menu in the top right and click **Custom repositories**.
|
||||||
4. Enter the repository URL: `https://github.com/talon2king/NestQuest`
|
4. Enter the repository URL: `https://github.com/Maniacs-Mansion/NestQuest`
|
||||||
5. Select category **Integration** and click **Add**.
|
5. Select category **Integration** and click **Add**.
|
||||||
6. Search for **NestQuest** and click **Download**.
|
6. Search for **NestQuest** and click **Download**.
|
||||||
7. Restart Home Assistant.
|
7. Restart Home Assistant.
|
||||||
|
|
||||||
> **Note:** Development happens in the private Gitea repository
|
> **Note:** Development happens in the private Gitea repository
|
||||||
> (`https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest`). The GitHub
|
> (`https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest`). The GitHub
|
||||||
> repository is a public mirror maintained for HACS distribution.
|
> repository is a private mirror maintained for HACS distribution.
|
||||||
|
|
||||||
### Manual Installation
|
### Manual Installation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,87 @@
|
|||||||
# NestQuest Release Notes
|
# NestQuest Release Notes
|
||||||
|
|
||||||
|
## Version 0.5.0 — 2026-09-18
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
Fifth release of NestQuest, promoting Features 06, 07, and 08 from `dev` onto 0.4.0 (quest model, children registry, presence engine):
|
||||||
|
|
||||||
|
- **Feature 06 — quest definitions and assignment:** business layer `quest_definitions.py` over the multi-assignee / multi-window model (D-008). Create/edit/deactivate definitions; assign/unassign children; query helpers for materialization. `ScheduleRule` ⇄ `schedule_rules` storage mapping in the DAO layer.
|
||||||
|
- **Feature 07 — instance materialization:** `materialize.py` fans out one instance per (definition, child, window, firing date) when the recurrence rule fires and the child is present. Idempotent on `(definition_id, child_id, due_date, window)`; skips completed instances; never generates past dates. HA-local `today` and configured `horizon_days` threaded end-to-end. Regeneration on definition/assignee/presence changes; daily rollover listener; startup backfill; `nestquest.regenerate` service.
|
||||||
|
- **Feature 08 — completion and event log:** business layer `completion.py` wrapping the existing append-only `CompletionEventsDao`. `complete_instance` / `uncomplete_instance` (reversal event, original row untouched); state derived from the latest event; HA-local `was_on_time`; derived `missed` (never stored). Actor shapes `'user'` / `'panel'` with `actor_child_id` (D-008). Permission gating of un-complete is Feature 09.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- Minimum Home Assistant version: **2024.6.0**.
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
- None at the schema layer. Schema remains **version 6** (advanced in 0.4.0). This release adds application code only.
|
||||||
|
|
||||||
|
### Known Limitations
|
||||||
|
|
||||||
|
- No permission decorator yet (Feature 09): `complete_instance` / `uncomplete_instance` are Python APIs, not gated HA services. The only HA service in this release is `nestquest.regenerate`.
|
||||||
|
- No entity or event surface (Feature 10): no sensors, no `nestquest_quest_completed` bus events.
|
||||||
|
- No panel or admin Lovelace cards (Features 12/13).
|
||||||
|
- Un-complete is implemented but not admin-gated until Feature 09.
|
||||||
|
- Single-instance only.
|
||||||
|
|
||||||
|
### Rollback
|
||||||
|
|
||||||
|
Schema version is unchanged from 0.4.0 (still 6). A code downgrade to 0.4.0 does not require a database restore for schema compatibility, but **back up `nestquest.db` (plus `-wal`/`-shm`) before upgrading** — generated instances and completion events written by 0.5.0 remain in the file after downgrade; 0.4.0 will not generate or complete them.
|
||||||
|
|
||||||
|
**HACS installs:**
|
||||||
|
|
||||||
|
1. Downgrade via HACS (HACS > Integrations > NestQuest > Redownload > 0.4.0).
|
||||||
|
2. Restart Home Assistant.
|
||||||
|
|
||||||
|
**Manual installs:**
|
||||||
|
|
||||||
|
1. Replace `custom_components/nestquest/` with the 0.4.0 tree.
|
||||||
|
2. Restart Home Assistant.
|
||||||
|
|
||||||
|
**Repository operators:** git-revert the 0.5.0 release merge on `main`.
|
||||||
|
|
||||||
|
## Version 0.4.0 — 2026-09-15
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
Fourth release of NestQuest, shipping three completed features on top of the data layer and recurrence engine:
|
||||||
|
|
||||||
|
- **Feature 14 — quest domain model (BREAKING rename, D-007/D-008):** `task_definitions`/`task_instances` are now `quest_definitions`/`quest_instances` everywhere; definitions are multi-assignee (`quest_definition_assignees`, no single child column) and multi-window (`quest_definition_windows` — morning/afternoon/evening, clock ranges in `const.py`); the instance key widens to `(definition_id, child_id, due_date, window)` so twice-daily and shared quests materialise one instance per child per window per day; `completion_events` gains `actor_child_id` (the tapped panel profile; admin events stay NULL) with a second actor-pair CHECK. Schema version advances to **6**.
|
||||||
|
- **Feature 03 — children registry & admin allowlist:** typed business layer for child profiles (create/edit with trimmed required names and duplicate-name warnings, whole-table reorder, deactivation as the only removal path — history is never touched); the admin allowlist with a fail-closed `is_admin` resolver (empty allowlist denies everyone; the last admin cannot be removed); automatic seeding on first setup (the config-flow user is persisted in entry data, with the options-flow list and HA owner accounts as fallbacks) so the owner is never locked out; the options flow gains a multi-select picker of existing Home Assistant users.
|
||||||
|
- **Feature 05 — presence & custody engine:** a pure `PresenceSchedule`/`PresenceOverride` model with total validation and lossless round-trips; anchor-date cycle arithmetic (D-004 — never ISO week numbers or parity); `PresenceEngine.is_present` with the always-present default for schedule-free children; date overrides beat the repeating pattern; a `next_present_dates` preview for the admin schedule UI; a full-year regression guard walking 2020 (366 days, ISO week 53) and 2015 (365 days, ISO week 53) proving the rotation never inverts at a year boundary.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
- Minimum Home Assistant version: **2024.6.0**.
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
- The database schema advances from version 1 to version 6. Upgrades are automatic and data-preserving (legacy `task_*` tables are renamed; the definitions table is rebuilt for multi-assignee; legacy instances pin to the morning window; panel events backfill `actor_child_id`). **Back up `nestquest.db` before upgrading** — see Rollback.
|
||||||
|
- Dev installs from before 2026-09-15 migrate automatically on first start.
|
||||||
|
|
||||||
|
### Known Limitations
|
||||||
|
|
||||||
|
- Still no entities, services, calendar platforms, or panel cards: nothing is user-visible in Home Assistant yet (Features 06-10 land the business layer, materialisation, permission gate, and entity surface).
|
||||||
|
- No materialisation yet: instances are not generated from definitions + presence yet (Feature 07).
|
||||||
|
- Single-instance only.
|
||||||
|
|
||||||
|
### Rollback
|
||||||
|
|
||||||
|
**Back up `nestquest.db` (plus `-wal`/`-shm` sidecars) BEFORE upgrading.** This release migrates the database to schema version 6 with renamed tables; an older NestQuest build refuses a version-6 database ("newer than this integration understands") and cannot read renamed tables at all — a plain downgrade is NOT possible once the database has been migrated.
|
||||||
|
|
||||||
|
**Rollback procedure (after a migrated database):**
|
||||||
|
|
||||||
|
1. Stop Home Assistant.
|
||||||
|
2. Restore the pre-upgrade `nestquest.db` backup (and sidecars) you made before upgrading.
|
||||||
|
3. Downgrade the integration (HACS > Integrations > NestQuest > Redownload > 0.3.0) or replace `custom_components/nestquest/` manually.
|
||||||
|
4. Restart Home Assistant.
|
||||||
|
|
||||||
|
Without a backup, the only path is staying on 0.4.0 (recommended) or deleting the integration and its database to start fresh (Settings > Devices & Services > NestQuest > Delete, then remove `nestquest.db`).
|
||||||
|
|
||||||
|
**Repository operators:** instead of uninstalling, git-revert the release merge on `main` and restore the pre-upgrade database.
|
||||||
|
|
||||||
## Version 0.3.0 — 2026-09-14
|
## Version 0.3.0 — 2026-09-14
|
||||||
|
|
||||||
### Scope
|
### Scope
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
"frontend",
|
"frontend",
|
||||||
"http"
|
"http"
|
||||||
],
|
],
|
||||||
"documentation": "https://github.com/talon2king/NestQuest",
|
"documentation": "https://github.com/Maniacs-Mansion/NestQuest",
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"issue_tracker": "https://github.com/talon2king/NestQuest/issues",
|
"issue_tracker": "https://github.com/Maniacs-Mansion/NestQuest/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"version": "0.3.0"
|
"version": "0.5.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ from collections.abc import Callable, Iterable
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
@@ -280,7 +279,6 @@ class NestQuestCompletionPctTodaySensor(_NestQuestChildDaySensor):
|
|||||||
"""The child's completion percentage for today (100 on a zero-quest day)."""
|
"""The child's completion percentage for today (100 on a zero-quest day)."""
|
||||||
|
|
||||||
_attr_state_class = SensorStateClass.MEASUREMENT
|
_attr_state_class = SensorStateClass.MEASUREMENT
|
||||||
_attr_device_class = SensorDeviceClass.PERCENTAGE
|
|
||||||
_attr_native_unit_of_measurement = "%"
|
_attr_native_unit_of_measurement = "%"
|
||||||
_name_suffix = "completion pct today"
|
_name_suffix = "completion pct today"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "nestquest"
|
name = "nestquest"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
description = "NestQuest Home Assistant Integration"
|
description = "NestQuest Home Assistant Integration"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
|
|||||||
+2
-2
@@ -67,9 +67,9 @@ def test_readme_exists_and_contains_required_sections() -> None:
|
|||||||
def test_readme_hacs_points_at_public_github_mirror() -> None:
|
def test_readme_hacs_points_at_public_github_mirror() -> None:
|
||||||
"""Verify README directs HACS users to the public GitHub mirror, not the private Gitea repo."""
|
"""Verify README directs HACS users to the public GitHub mirror, not the private Gitea repo."""
|
||||||
content = README_PATH.read_text(encoding="utf-8")
|
content = README_PATH.read_text(encoding="utf-8")
|
||||||
assert "https://github.com/talon2king/NestQuest" in content
|
assert "https://github.com/Maniacs-Mansion/NestQuest" in content
|
||||||
assert "https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest" in content
|
assert "https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest" in content
|
||||||
assert content.index("https://github.com/talon2king/NestQuest") < content.index(
|
assert content.index("https://github.com/Maniacs-Mansion/NestQuest") < content.index(
|
||||||
"https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest"
|
"https://code.cubecraftlabs.com/Maniacs_Mansion/NestQuest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -58,19 +58,19 @@ def test_manifest_requirements(manifest: dict) -> None:
|
|||||||
|
|
||||||
def test_manifest_documentation_url_is_public_github_mirror(manifest: dict) -> None:
|
def test_manifest_documentation_url_is_public_github_mirror(manifest: dict) -> None:
|
||||||
"""Verify documentation URL points at the public GitHub mirror."""
|
"""Verify documentation URL points at the public GitHub mirror."""
|
||||||
assert manifest.get("documentation") == "https://github.com/talon2king/NestQuest"
|
assert manifest.get("documentation") == "https://github.com/Maniacs-Mansion/NestQuest"
|
||||||
|
|
||||||
|
|
||||||
def test_manifest_issue_tracker_url_is_public_github_mirror(manifest: dict) -> None:
|
def test_manifest_issue_tracker_url_is_public_github_mirror(manifest: dict) -> None:
|
||||||
"""Verify issue_tracker URL points at the public GitHub mirror."""
|
"""Verify issue_tracker URL points at the public GitHub mirror."""
|
||||||
assert manifest.get("issue_tracker") == "https://github.com/talon2king/NestQuest/issues"
|
assert manifest.get("issue_tracker") == "https://github.com/Maniacs-Mansion/NestQuest/issues"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("key", ["documentation", "issue_tracker"])
|
@pytest.mark.parametrize("key", ["documentation", "issue_tracker"])
|
||||||
def test_manifest_urls_use_github_com(manifest: dict, key: str) -> None:
|
def test_manifest_urls_use_github_com(manifest: dict, key: str) -> None:
|
||||||
"""Verify manifest URLs use github.com rather than the private Gitea host."""
|
"""Verify manifest URLs use github.com rather than the private Gitea host."""
|
||||||
url = manifest.get(key, "")
|
url = manifest.get(key, "")
|
||||||
assert url.startswith("https://github.com/talon2king/NestQuest")
|
assert url.startswith("https://github.com/Maniacs-Mansion/NestQuest")
|
||||||
assert "cubecraftlabs" not in url
|
assert "cubecraftlabs" not in url
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ async def test_every_active_child_gets_five_sensors_with_documented_identity(
|
|||||||
assert entity.native_unit_of_measurement is None
|
assert entity.native_unit_of_measurement is None
|
||||||
assert pct_entity.state_class == "measurement"
|
assert pct_entity.state_class == "measurement"
|
||||||
assert pct_entity.native_unit_of_measurement == "%"
|
assert pct_entity.native_unit_of_measurement == "%"
|
||||||
assert pct_entity.device_class == "percentage"
|
assert pct_entity.device_class is None
|
||||||
device_info = due_entity.device_info
|
device_info = due_entity.device_info
|
||||||
assert device_info.name == f"NestQuest {name}"
|
assert device_info.name == f"NestQuest {name}"
|
||||||
assert device_info.manufacturer == "NestQuest"
|
assert device_info.manufacturer == "NestQuest"
|
||||||
|
|||||||
Reference in New Issue
Block a user