New apps Added

This commit is contained in:
2026-07-08 10:43:39 -04:00
parent 3b1f4bbd75
commit fefc2c8b5c
1114 changed files with 406637 additions and 154 deletions
+251
View File
@@ -0,0 +1,251 @@
"""Button platform for TaskMate integration."""
from __future__ import annotations
import logging
from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import TaskMateCoordinator
from .models import Child, Chore, Reward
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up TaskMate buttons."""
coordinator: TaskMateCoordinator = hass.data[DOMAIN][entry.entry_id]
entities: list[ButtonEntity] = []
# Add complete chore buttons for each child/chore combination
children = coordinator.data.get("children", [])
chores = coordinator.data.get("chores", [])
rewards = coordinator.data.get("rewards", [])
for child in children:
# Chore completion buttons
for chore in chores:
if getattr(chore, "assignment_mode", "everyone") == "unassigned":
continue
if not chore.assigned_to or child.id in chore.assigned_to:
entities.append(
CompleteChoreButton(coordinator, entry, child, chore)
)
# Reward claim buttons
for reward in rewards:
entities.append(
ClaimRewardButton(coordinator, entry, child, reward)
)
# Track which entity combos already exist
tracked_combos: set[str] = set()
for child in children:
for chore in chores:
if getattr(chore, "assignment_mode", "everyone") == "unassigned":
continue
if not chore.assigned_to or child.id in chore.assigned_to:
tracked_combos.add(f"{child.id}_{chore.id}_complete")
for reward in rewards:
tracked_combos.add(f"{child.id}_{reward.id}_claim")
async_add_entities(entities)
# Set up listener to add buttons for new children/chores/rewards
@callback
def async_update_entities() -> None:
"""Add button entities for newly created children, chores, or rewards."""
new_entities: list[ButtonEntity] = []
current_children = coordinator.data.get("children", [])
current_chores = coordinator.data.get("chores", [])
current_rewards = coordinator.data.get("rewards", [])
for child in current_children:
for chore in current_chores:
if getattr(chore, "assignment_mode", "everyone") == "unassigned":
continue
if not chore.assigned_to or child.id in chore.assigned_to:
key = f"{child.id}_{chore.id}_complete"
if key not in tracked_combos:
new_entities.append(
CompleteChoreButton(coordinator, entry, child, chore)
)
tracked_combos.add(key)
for reward in current_rewards:
key = f"{child.id}_{reward.id}_claim"
if key not in tracked_combos:
new_entities.append(
ClaimRewardButton(coordinator, entry, child, reward)
)
tracked_combos.add(key)
if new_entities:
async_add_entities(new_entities)
coordinator.async_add_listener(async_update_entities)
class TaskMateBaseButton(CoordinatorEntity, ButtonEntity):
"""Base class for TaskMate buttons."""
def __init__(
self,
coordinator: TaskMateCoordinator,
entry: ConfigEntry,
) -> None:
"""Initialize the button."""
super().__init__(coordinator)
self._entry = entry
@property
def device_info(self) -> DeviceInfo:
"""Return device info."""
return DeviceInfo(
identifiers={(DOMAIN, self._entry.entry_id)},
name="TaskMate",
manufacturer="TaskMate",
model="Family Chore Manager",
)
class CompleteChoreButton(TaskMateBaseButton):
"""Button to mark a chore as completed."""
def __init__(
self,
coordinator: TaskMateCoordinator,
entry: ConfigEntry,
child: Child,
chore: Chore,
) -> None:
"""Initialize the button."""
super().__init__(coordinator, entry)
self.child_id = child.id
self.chore_id = chore.id
self._attr_unique_id = f"{entry.entry_id}_{child.id}_{chore.id}_complete"
self._attr_name = f"{child.name}: Complete {chore.name}"
@property
def icon(self) -> str:
"""Return the icon."""
chore = self.coordinator.get_chore(self.chore_id)
return getattr(chore, 'icon', "mdi:check-circle") if chore else "mdi:check-circle"
@property
def extra_state_attributes(self) -> dict:
"""Return additional attributes."""
child = self.coordinator.get_child(self.child_id)
chore = self.coordinator.get_chore(self.chore_id)
if not child or not chore:
return {}
return {
"child_id": child.id,
"child_name": child.name,
"chore_id": chore.id,
"chore_name": chore.name,
"points": chore.points,
"requires_approval": chore.requires_approval,
}
async def async_press(self) -> None:
"""Handle the button press."""
try:
await self.coordinator.async_complete_chore(self.chore_id, self.child_id)
except ValueError as err:
_LOGGER.error("Failed to complete chore: %s", err)
class ClaimRewardButton(TaskMateBaseButton):
"""Button to claim a reward."""
def __init__(
self,
coordinator: TaskMateCoordinator,
entry: ConfigEntry,
child: Child,
reward: Reward,
) -> None:
"""Initialize the button."""
super().__init__(coordinator, entry)
self.child_id = child.id
self.reward_id = reward.id
self._attr_unique_id = f"{entry.entry_id}_{child.id}_{reward.id}_claim"
self._attr_name = f"{child.name}: Claim {reward.name}"
@property
def icon(self) -> str:
"""Return the icon."""
reward = self.coordinator.get_reward(self.reward_id)
return reward.icon if reward else "mdi:gift"
def _get_committed_points(self, child_id: str) -> int:
"""Calculate points committed by pending reward claims for a child.
Pool-mode pending claims are skipped because their cost was already
deducted from child.points at allocation time.
"""
pending_claims = self.coordinator.data.get("pending_reward_claims", [])
committed = 0
for c in pending_claims:
if c.child_id == child_id and not self.coordinator.is_pool_mode_claim(c):
reward = self.coordinator.get_reward(c.reward_id)
if reward:
committed += reward.cost
return committed
@property
def available(self) -> bool:
"""Return if button is available (accounting for committed points)."""
child = self.coordinator.get_child(self.child_id)
reward = self.coordinator.get_reward(self.reward_id)
if not child or not reward:
return False
available_points = child.points - self._get_committed_points(child.id)
return available_points >= reward.cost
@property
def extra_state_attributes(self) -> dict:
"""Return additional attributes."""
child = self.coordinator.get_child(self.child_id)
reward = self.coordinator.get_reward(self.reward_id)
if not child or not reward:
return {}
committed = self._get_committed_points(child.id)
available_points = child.points - committed
can_afford = available_points >= reward.cost
return {
"child_id": child.id,
"child_name": child.name,
"reward_id": reward.id,
"reward_name": reward.name,
"cost": reward.cost,
"child_points": child.points,
"committed_points": committed,
"available_points": available_points,
"can_afford": can_afford,
"points_needed": max(0, reward.cost - available_points),
}
async def async_press(self) -> None:
"""Handle the button press."""
try:
await self.coordinator.async_claim_reward(self.reward_id, self.child_id)
except ValueError as err:
_LOGGER.error("Failed to claim reward: %s", err)