"""Constants for TaskMate integration.""" from typing import Final DOMAIN: Final = "taskmate" CONF_TASK_GROUP_ID: Final = "group_id" CONF_TASK_GROUP_NAME: Final = "name" CONF_TASK_GROUP_POLICY: Final = "policy" CONF_TASK_GROUP_CHORE_IDS: Final = "chore_ids" TASK_GROUP_POLICIES: Final = ["sticky", "spread"] # Calendar projection: how many days ahead each chore's assignment is pushed # into the configured HA calendars. 14 days balances planning visibility with # calendar noise; raise via the Settings flow for longer planning horizons. DEFAULT_CALENDAR_PROJECTION_DAYS: Final = 14 MIN_CALENDAR_PROJECTION_DAYS: Final = 1 MAX_CALENDAR_PROJECTION_DAYS: Final = 90 # Assignment modes # "everyone" = visible to every child in assigned_to (or all children if empty) -- existing behavior # "alternating" = round-robin through assigned_to (or all children), one child per calendar day # "random" = deterministic per-day random pick from the same pool (each chore picks independently) # "balanced" = split today's balanced-mode chores evenly across the pool so no one child gets swamped # "first_come" = competitive: shown to the whole pool at once; first child to complete it wins and it # hides for everyone else (shared quota of 1). A parent rejection reopens it for the pool. ASSIGNMENT_MODES: Final = ["everyone", "alternating", "random", "balanced", "first_come", "unassigned"] # Teamwork chores (#928): how a finished team's points are shared out. # "each" = every participant earns the chore's full points # "split" = the points are divided evenly across the team, rounded down TEAM_POINTS_MODES: Final = ["each", "split"] # Upper bound on team_size — more than a family has children is meaningless, # and the joins list rides in a sensor attribute. TEAM_SIZE_MAX: Final = 10 # Bounty board (#931): one-off jobs any eligible child can claim. # A claim locks the bounty to one child for claim_hours (never past its # expiry); an unfinished claim lapses back onto the board. BOUNTY_STATUSES: Final = ["open", "claimed", "pending", "completed", "expired"] BOUNTY_CLAIM_HOURS_DEFAULT: Final = 2 BOUNTY_CLAIM_HOURS_MAX: Final = 48 BOUNTY_POINTS_MAX: Final = 100000 BOUNTY_TITLE_MAX_LENGTH: Final = 120 BOUNTY_DESCRIPTION_MAX_LENGTH: Final = 500 # The claimer is warned this long before their claim lapses (opt-in type). BOUNTY_LAPSE_WARNING_MINUTES: Final = 15 # Approved bounties stay on the card as "Recently completed" this long. BOUNTY_RECENT_HOURS: Final = 24 # Chore auctions (#982): children bid the fewest points they'd accept for one # occurrence of a chore; the lowest bid wins it at that price. AUCTION_STATUSES: Final = ["open", "closed", "cancelled"] AUCTION_POINTS_MAX: Final = 100000 # Eligible children get the "closing soon" push this long before bidding ends. AUCTION_REMINDER_MINUTES: Final = 60 # Closed auctions stay on the children's cards as results this long. AUCTION_RESULTS_HOURS: Final = 24 # Default values DEFAULT_POINTS_NAME: Final = "Stars" DEFAULT_POINTS_ICON: Final = "mdi:star" # Schedule modes SCHEDULE_MODES: Final = ["specific_days", "recurring", "one_shot"] # Nominal length of each Mode-B recurrence period, in days. Used for report # expectations and recurrence math (not for the shorter availability window # in coord_chores.is_chore_available_for_child, which is deliberately capped). RECURRENCE_PERIOD_DAYS: Final = { "every_2_days": 2, "weekly": 7, "every_2_weeks": 14, "monthly": 30, "every_3_months": 91, "every_6_months": 182, } # Badge tiers, lowest to highest. BADGE_TIERS: Final = ["bronze", "silver", "gold", "platinum"] # Time categories for chores TIME_CATEGORIES: Final = [ "morning", "afternoon", "evening", "night", "anytime", ] # Time category icons TIME_CATEGORY_ICONS: Final = { "morning": "mdi:weather-sunny", "afternoon": "mdi:white-balance-sunny", "evening": "mdi:weather-sunset", "night": "mdi:weather-night", "anytime": "mdi:clock-outline", } # Default user-editable time-of-day periods. "anytime" is implicit (all-day, # always available) and never appears in this list. An empty label means # "use the translated built-in name for this id". DEFAULT_TIME_PERIODS: Final = [ {"id": "morning", "label": "", "start": "06:00", "end": "12:00", "icon": "mdi:weather-sunny"}, {"id": "afternoon", "label": "", "start": "12:00", "end": "17:00", "icon": "mdi:white-balance-sunny"}, {"id": "evening", "label": "", "start": "17:00", "end": "21:00", "icon": "mdi:weather-sunset"}, {"id": "night", "label": "", "start": "21:00", "end": "23:59", "icon": "mdi:weather-night"}, ] MAX_TIME_PERIODS: Final = 24 # Open-ended chores (#832): the child describes the work and suggests what it # was worth. Both are child-entered free input, so they're bounded before storage. CHORE_NOTE_MAX_LENGTH: Final = 200 # Admin panel Today page (#966): days of per-child done/total snapshots kept. DAILY_PROGRESS_KEEP_DAYS: Final = 30 CHORE_SUGGESTED_POINTS_MAX: Final = 999 # NFC / QR tag completion (#923): a chore lists the HA tag ids that complete it. # Tag ids are typed or picked by a parent, so both the count and the length are # bounded before storage. MAX_CHORE_TAGS: Final = 10 TAG_ID_MAX_LENGTH: Final = 100 # A phone reading an NFC sticker often fires tag_scanned twice for one tap, so a # second scan of the same chore by the same child inside this window is ignored # even when the daily limit would allow another completion. TAG_SCAN_DEBOUNCE_SECONDS: Final = 60 EVENT_TAG_COMPLETION: Final = "taskmate_tag_completion" # Platforms PLATFORMS: Final = ["sensor", "button", "binary_sensor"] # Services SERVICE_COMPLETE_CHORE: Final = "complete_chore" SERVICE_APPROVE_CHORE: Final = "approve_chore" SERVICE_APPROVE_ALL_CHORES: Final = "approve_all_chores" SERVICE_REJECT_CHORE: Final = "reject_chore" SERVICE_UNDO_CHORE: Final = "undo_chore" SERVICE_UNDO_CHORE_APPROVAL: Final = "undo_chore_approval" SERVICE_APPLY_MANDATORY_PENALTY: Final = "apply_mandatory_penalty" SERVICE_POSTPONE_MANDATORY_CHORE: Final = "postpone_mandatory_chore" SERVICE_DISMISS_MANDATORY_CHORE: Final = "dismiss_mandatory_chore" SERVICE_CLAIM_REWARD: Final = "claim_reward" SERVICE_APPROVE_REWARD: Final = "approve_reward" SERVICE_REJECT_REWARD: Final = "reject_reward" SERVICE_ADD_POINTS: Final = "add_points" SERVICE_REMOVE_POINTS: Final = "remove_points" SERVICE_UNDO_TRANSACTION: Final = "undo_transaction" SERVICE_TEST_NOTIFICATION: Final = "test_notification" SERVICE_GIFT_POINTS: Final = "gift_points" SERVICE_ADJUST_STREAK_FREEZES: Final = "adjust_streak_freezes" SERVICE_RECORD_ALLOWANCE_PAYOUT: Final = "record_allowance_payout" SERVICE_REQUEST_SWAP: Final = "request_swap" SERVICE_SPIN_ROULETTE: Final = "spin_roulette" SERVICE_READ_ALOUD: Final = "read_aloud" SERVICE_CHOOSE_AVATAR: Final = "choose_avatar" SERVICE_SET_CHORE_ORDER: Final = "set_chore_order" SERVICE_PREVIEW_SOUND: Final = "preview_sound" SERVICE_ADD_PENALTY: Final = "add_penalty" SERVICE_UPDATE_PENALTY: Final = "update_penalty" SERVICE_REMOVE_PENALTY: Final = "remove_penalty" SERVICE_APPLY_PENALTY: Final = "apply_penalty" SERVICE_ADD_BONUS: Final = "add_bonus" SERVICE_UPDATE_BONUS: Final = "update_bonus" SERVICE_REMOVE_BONUS: Final = "remove_bonus" SERVICE_APPLY_BONUS: Final = "apply_bonus" SERVICE_ADD_CHORE: Final = "add_chore" SERVICE_ALLOCATE_POINTS_TO_POOL: Final = "allocate_points_to_pool" SERVICE_SKIP_CHORE: Final = "skip_chore" SERVICE_SET_CHORE_MANUAL_START: Final = "set_chore_manual_start" SERVICE_ADD_TASK_GROUP: Final = "add_task_group" SERVICE_UPDATE_TASK_GROUP: Final = "update_task_group" SERVICE_REMOVE_TASK_GROUP: Final = "remove_task_group" SERVICE_COMPLETE_BONUS_SUBTASK: Final = "complete_bonus_subtask" SERVICE_START_TIMED_TASK: Final = "start_timed_task" SERVICE_PAUSE_TIMED_TASK: Final = "pause_timed_task" SERVICE_STOP_TIMED_TASK: Final = "stop_timed_task" SERVICE_LEAVE_TEAM_CHORE: Final = "leave_team_chore" SERVICE_COMPLETE_NEXT_CHORE: Final = "complete_next_chore" SERVICE_POST_BOUNTY: Final = "post_bounty" SERVICE_UPDATE_BOUNTY: Final = "update_bounty" SERVICE_REMOVE_BOUNTY: Final = "remove_bounty" SERVICE_CLAIM_BOUNTY: Final = "claim_bounty" SERVICE_GIVE_BACK_BOUNTY: Final = "give_back_bounty" SERVICE_COMPLETE_BOUNTY: Final = "complete_bounty" # Events EVENT_PREVIEW_SOUND: Final = "taskmate_preview_sound" # Attributes ATTR_CHILD_ID: Final = "child_id" ATTR_CHORE_ID: Final = "chore_id" ATTR_AS_PARENT: Final = "as_parent" ATTR_REWARD_ID: Final = "reward_id" ATTR_POINTS: Final = "points" ATTR_REASON: Final = "reason" ATTR_CHORE_ORDER: Final = "chore_order" ATTR_SOUND: Final = "sound" ATTR_PENALTY_ID: Final = "penalty_id" ATTR_PENALTY_NAME: Final = "name" ATTR_PENALTY_POINTS: Final = "points" ATTR_PENALTY_DESCRIPTION: Final = "description" ATTR_PENALTY_ICON: Final = "icon" ATTR_PENALTY_ASSIGNED_TO: Final = "assigned_to" ATTR_BONUS_ID: Final = "bonus_id" ATTR_BONUS_NAME: Final = "name" ATTR_BONUS_POINTS: Final = "points" ATTR_BONUS_DESCRIPTION: Final = "description" ATTR_BONUS_ICON: Final = "icon" ATTR_BONUS_ASSIGNED_TO: Final = "assigned_to" ATTR_CHORE_NAME: Final = "name" ATTR_CHORE_DESCRIPTION: Final = "description" ATTR_CHORE_POINTS: Final = "points" ATTR_CHORE_ASSIGNED_TO: Final = "assigned_to" ATTR_CHORE_TIME_CATEGORY: Final = "time_category" ATTR_CHORE_ONE_SHOT: Final = "one_shot" ATTR_CHORE_REQUIRES_APPROVAL: Final = "requires_approval" ATTR_CHORE_EXPIRES_IN_MINUTES: Final = "expires_in_minutes" ATTR_CHORE_SPEED_BONUS_POINTS: Final = "speed_bonus_points" ATTR_BONUS_SUBTASK_ID: Final = "bonus_subtask_id" # Badge attributes ATTR_BADGE_ID: Final = "badge_id" ATTR_BADGE_NAME: Final = "name" ATTR_BADGE_DESCRIPTION: Final = "description" ATTR_BADGE_ICON: Final = "icon" ATTR_BADGE_TIER: Final = "tier" ATTR_BADGE_POINT_BONUS: Final = "point_bonus" ATTR_BADGE_CRITERIA: Final = "criteria" ATTR_BADGE_COMBINATOR: Final = "combinator" ATTR_BADGE_ASSIGNED_TO: Final = "assigned_to" ATTR_BADGE_NOTIFY_ON_EARN: Final = "notify_on_earn" ATTR_BADGE_ENABLED: Final = "enabled" ATTR_AWARDED_BADGE_ID: Final = "awarded_badge_id" # Completion sound options # Most sounds are synthesized via Web Audio API # Fart sounds are CC0 audio files from BigSoundBank.com and GfxSounds.com COMPLETION_SOUND_OPTIONS: Final = [ "none", # No sound "coin", # Coin collect sound "levelup", # Level up / success sound "fanfare", # Celebratory fanfare "chime", # Simple chime "powerup", # Power up sound "undo", # Sad/descending "womp womp" for undo actions "fart1", # Flatulence 1 (short) "fart2", # Flatulence 2 (short) "fart3", # Flatulence 3 (short) "fart4", # Pony flatulence 2 (~3 sec) "fart5", # Flatulence 4 - discreet (short) "fart6", # Prout'cochons 1 - pig game sound (short) "fart7", # Prout'cochons 2 - pig game sound (short) "fart8", # Prout'cochons 3 - pig game sound (short) "fart9", # Pony flatulence 1 (short) "fart10", # Baby fart (short) "fart_random", # Random fart - picks a random fart sound each time! ] def is_valid_completion_sound(value: str | None) -> bool: """True for a built-in sound name or a well-formed custom sound reference. A chore's ``completion_sound`` is either one of ``COMPLETION_SOUND_OPTIONS`` or ``custom:<32 hex>.`` pointing at an uploaded file (#856). Kept here rather than in ``sounds.py`` so schema code can validate the whole field from one import. The custom check is pure, so it is safe on untrusted input. """ if value in COMPLETION_SOUND_OPTIONS: return True from .sounds import is_custom_sound return is_custom_sound(value) # --- Chore difficulty tiers --- # Each chore carries a difficulty tier; the points it awards are the base # points multiplied by the tier's multiplier. "medium" is the neutral baseline # (×1.0) so chores that predate this feature (which default to medium) keep # their exact award value. Multipliers are configurable via the settings # "difficulty_multiplier_" keys. DIFFICULTY_TIERS: Final = ("easy", "medium", "hard") DEFAULT_DIFFICULTY: Final = "medium" DEFAULT_DIFFICULTY_MULTIPLIERS: Final = {"easy": 0.5, "medium": 1.0, "hard": 2.0} # Setup wizard age groups (#980), youngest first. A child's age group only # decides which chores the wizard suggests; "" means none picked. It is stored # separately from the birthday so an age without a date never becomes a fake # birthday (which would set off birthday mode on the wrong day). AGE_GROUPS: Final = ("3_5", "6_8", "9_12", "13_plus") # --- Chore quality rating (#927) --- # When the "quality_rating_enabled" setting is on, a parent may rate an approval # 1-3 stars and the chore's base points are scaled by that star's multiplier # (configurable via the "quality_rating_multiplier_" settings keys). An # unrated approval pays 100%, so turning the feature on changes nothing until a # parent actually picks a star. QUALITY_RATINGS: Final = (1, 2, 3) DEFAULT_QUALITY_RATING_MULTIPLIERS: Final = {1: 0.75, 2: 1.0, 3: 1.25} # --- Notification type IDs (v3.9.0) --- NOTIF_TYPE_BEDTIME_REMINDER: Final = "bedtime_reminder" NOTIF_TYPE_STREAK_AT_RISK: Final = "streak_at_risk" NOTIF_TYPE_ALL_CHORES_DONE: Final = "all_chores_done" NOTIF_TYPE_BADGE_EARNED: Final = "badge_earned" NOTIF_TYPE_PENDING_CHORE_APPROVAL: Final = "pending_chore_approval" NOTIF_TYPE_PENDING_REWARD_CLAIM: Final = "pending_reward_claim" NOTIF_TYPE_STREAK_MILESTONE: Final = "streak_milestone" NOTIF_TYPE_LEVEL_UP: Final = "level_up" NOTIF_TYPE_WEEKLY_DIGEST: Final = "weekly_digest" NOTIF_TYPE_CELEBRATION: Final = "celebration" NOTIF_TYPE_MANDATORY_REMINDER: Final = "mandatory_reminder" NOTIF_TYPE_MANDATORY_PARENT_ALERT: Final = "mandatory_parent_alert" NOTIF_TYPE_MONTHLY_REPORT: Final = "monthly_report" NOTIF_TYPE_SEASON_CHAMPION: Final = "season_champion" NOTIF_TYPE_FAMILY_GOAL_REACHED: Final = "family_goal_reached" NOTIF_TYPE_BIRTHDAY: Final = "birthday" NOTIF_TYPE_STREAK_FREEZE_USED: Final = "streak_freeze_used" NOTIF_TYPE_PRESENCE_ARRIVAL: Final = "presence_arrival" NOTIF_TYPE_BOUNTY_POSTED: Final = "bounty_posted" NOTIF_TYPE_BOUNTY_CLAIM_LAPSING: Final = "bounty_claim_lapsing" NOTIF_TYPE_RECAP_READY: Final = "recap_ready" # Reject reasons (#976): tells a child a chore or reward claim was sent back, # with the parent's reason when one was given. NOTIF_TYPE_ITEM_REJECTED: Final = "item_rejected" # Chore auctions (#982): an auction opened, bidding closes within the hour, # and the result (or a cancellation). NOTIF_TYPE_AUCTION_OPENED: Final = "auction_opened" NOTIF_TYPE_AUCTION_CLOSING: Final = "auction_closing" NOTIF_TYPE_AUCTION_RESULT: Final = "auction_result" # Surprise inspections (#981): the child hears an inspection is coming (only # when they're told) and that it passed; the parent is reminded 30 minutes # before an undecided inspection closes. NOTIF_TYPE_INSPECTION_STARTED: Final = "inspection_started" NOTIF_TYPE_INSPECTION_PASSED: Final = "inspection_passed" NOTIF_TYPE_INSPECTION_REMINDER: Final = "inspection_reminder" # Presence-aware reminders (#926): the default for how long a child must have # been away before arriving home earns a "you're home" nudge. DEFAULT_PRESENCE_ARRIVAL_MIN_AWAY: Final = 30 # Default notification tap target. Must match PANEL_URL_PATH in panel.py — # a bare /taskmate is the static-files prefix and returns 403, not the panel. DEFAULT_NOTIFICATION_NAV_URL: Final = "/taskmate-admin" # Default notification group (#811). The HA companion app stacks notifications # that share this key, so TaskMate's alerts collapse into one bundle instead of # scattering through the rest of the phone's HA notifications. Applied by # default — set it to "" in the panel to turn grouping off. DEFAULT_NOTIFICATION_GROUP: Final = "taskmate" # --- Wishlist with pledges (#932) --- # Wishes a child has open at once: waiting for approval, saving, or waiting # for the parent to hand the thing over. WISH_MAX_OPEN_PER_CHILD: Final = 5 WISH_MAX_TARGET: Final = 100000 # Pledges on one wish; each is typed by a parent, so this only bounds abuse. WISH_MAX_PLEDGES: Final = 50 # Finished wishes kept per child: declined ones stay on the card (with the # reason) until dismissed, redeemed ones are the panel's history. WISH_KEEP_DECLINED_PER_CHILD: Final = 3 WISH_KEEP_REDEEMED_PER_CHILD: Final = 20 NOTIF_TYPE_WISH_REQUESTED: Final = "wish_requested" NOTIF_TYPE_WISH_PLEDGED: Final = "wish_pledged" SERVICE_ADD_WISH: Final = "add_wish" SERVICE_WITHDRAW_WISH: Final = "withdraw_wish" SERVICE_MOVE_POINTS_TO_WISH: Final = "move_points_to_wish" SERVICE_TAKE_POINTS_FROM_WISH: Final = "take_points_from_wish" SERVICE_REQUEST_WISH_REDEEM: Final = "request_wish_redeem" SERVICE_APPROVE_WISH: Final = "approve_wish" SERVICE_DECLINE_WISH: Final = "decline_wish" SERVICE_PLEDGE_TO_WISH: Final = "pledge_to_wish" SERVICE_REMOVE_WISH_PLEDGE: Final = "remove_wish_pledge" SERVICE_REMOVE_WISH: Final = "remove_wish"