Add the party board card (1a) read-only

Add the party board card (1a) read-only
This commit was merged in pull request #125.
This commit is contained in:
2026-09-20 12:39:47 -04:00
parent 89f517b3ad
commit 1911b2bf6e
6 changed files with 1923 additions and 220 deletions
+52 -2
View File
@@ -6,7 +6,7 @@ import datetime
from conftest import wire_entry_to_registry
from custom_components.nestquest import async_setup_entry
from custom_components.nestquest.children import create_child
from custom_components.nestquest.children import create_child, list_children
from custom_components.nestquest.const import DOMAIN
from custom_components.nestquest.dao_presence import PresenceSchedulesDao
from custom_components.nestquest.materialize import materialize
@@ -128,7 +128,57 @@ async def test_binary_sensor_identity_and_attributes(hass, make_entry) -> None:
assert present.name == "NestQuest Ada present today"
for entity in (all_done, present):
assert entity.device_info.name == "NestQuest Ada"
assert entity.extra_state_attributes == {
assert all_done.extra_state_attributes == {
"child_id": ada.id,
"child_name": "Ada",
}
assert present.extra_state_attributes == {
"child_id": ada.id,
"child_name": "Ada",
"next_present": None,
}
async def test_present_today_carries_next_present_when_away(
hass, make_entry
) -> None:
"""A child away today carries the ISO date of their next present
day (the panel's away plate renders ``Returns <weekday>, <Mon D>``
from it); an always-absent pattern carries ``None``."""
entry = await _setup_entry(hass, make_entry)
coordinator = entry.runtime_data.coordinator
database = entry.runtime_data.database
ada, _bo = await _seed_two_children(database)
await create_child(database, "Cory")
today = datetime.date.today()
pattern = ",".join(
str(day) for day in range(7) if day != today.weekday()
)
await PresenceSchedulesDao(database).upsert_by_child(
ada.id, 1, today.isoformat(), pattern
)
await PresenceSchedulesDao(database).upsert_by_child(
await _cory_id(database), 1, today.isoformat(), ""
)
await coordinator.async_refresh()
assert _binary(hass, ada.id, "present_today").is_on is False
assert _binary(hass, ada.id, "present_today").extra_state_attributes[
"next_present"
] == (today + datetime.timedelta(days=1)).isoformat()
cory = [
child for child in coordinator.data.children
if child.child_name == "Cory"
][0]
assert _binary(hass, cory.child_id, "present_today").is_on is False
assert _binary(hass, cory.child_id, "present_today").extra_state_attributes[
"next_present"
] is None
async def _cory_id(database) -> int:
for child in await list_children(database):
if child.display_name == "Cory":
return child.id
raise AssertionError("Cory was not created")
+65
View File
@@ -78,9 +78,43 @@ async def test_coordinator_snapshot_counts_and_states(hass, make_entry) -> None:
assert instance.title == "Brush teeth"
assert instance.window == "morning"
assert ada.present is True, "no schedule means present every day"
assert ada.next_present is None, "present child has no returns date"
assert snapshot.cycle_day == 0, "no schedule anywhere"
async def test_coordinator_next_present_for_away_child(
hass, make_entry
) -> None:
"""A child absent today carries the ISO date of their next present
day; an always-absent pattern carries None instead."""
entry = wire_entry_to_registry(make_entry(), hass.registry)
assert await async_setup_entry(hass, entry) is True
coordinator = entry.runtime_data.coordinator
database = entry.runtime_data.database
await create_child(database, "Ada")
ada = (await list_children(database))[0]
today = datetime.date.today()
pattern = ",".join(
str(day) for day in range(7) if day != today.weekday()
)
await PresenceSchedulesDao(database).upsert_by_child(
ada.id, 1, today.isoformat(), pattern
)
await create_child(database, "Bo")
bo = (await list_children(database))[1]
await PresenceSchedulesDao(database).upsert_by_child(
bo.id, 1, today.isoformat(), ""
)
snapshot = await coordinator._async_update_data()
by_name = {child.child_name: child for child in snapshot.children}
assert by_name["Ada"].present is False
assert by_name["Ada"].next_present == (
today + datetime.timedelta(days=1)
).isoformat()
assert by_name["Bo"].present is False
assert by_name["Bo"].next_present is None
async def test_coordinator_reflects_completion_and_uncompletion(
hass, make_entry
) -> None:
@@ -184,6 +218,37 @@ async def test_coordinator_presence_resolves_schedule_and_override(
assert snapshot.children[0].present is True
async def test_coordinator_next_present_honours_future_override(
hass, make_entry
) -> None:
entry = wire_entry_to_registry(make_entry(), hass.registry)
assert await async_setup_entry(hass, entry) is True
coordinator = entry.runtime_data.coordinator
database = entry.runtime_data.database
await create_child(database, "Ada")
ada = (await list_children(database))[0]
today = datetime.date.today()
tomorrow = today + datetime.timedelta(days=1)
# Scheduled present tomorrow (single-day cycle, weekday set set below
# covers every day), but overridden absent tomorrow.
await PresenceSchedulesDao(database).upsert_by_child(
ada.id, 1, today.isoformat(), "0,1,2,3,4,5,6"
)
from custom_components.nestquest.dao_presence import (
PresenceOverridesDao,
)
await PresenceOverridesDao(database).create(
ada.id, tomorrow.isoformat(), tomorrow.isoformat(), False
)
snapshot = await coordinator._async_update_data()
child = snapshot.children[0]
assert child.present is True
assert child.next_present != tomorrow.isoformat(), (
"a future absent override must remove tomorrow from the preview"
)
async def test_coordinator_derives_missed_for_past_due_open(
hass, make_entry
) -> None: