Files
HomeAssistantVS/custom_components/taskmate/const.py
T

376 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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>.<ext>`` 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_<tier>" 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_<n>" 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"