python-casahome 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ """Static availability detail, never slot submission. UNVERIFIED wire shape.
2
+
3
+ Fields inferred from the app. Status remains a string so new values survive.
4
+ Integer times are retained without promoting their units to observed evidence.
5
+ """
6
+
7
+ from casahome.models._base import CasaModel
8
+
9
+
10
+ class MemberAvailabilitySummary(CasaModel):
11
+ id: str
12
+ request_id: str
13
+ home_id: str
14
+ status: str
15
+ slot_count: int
16
+ first_submitted_at: int | None = None
17
+ last_submitted_at: int | None = None
18
+ created_at: int
19
+
20
+
21
+ class MemberAvailabilitySlot(CasaModel):
22
+ id: str
23
+ member_availability_request_id: str
24
+ start_time: int
25
+ end_time: int
26
+
27
+
28
+ class MemberAvailabilityConstraints(CasaModel):
29
+ snap_minutes: int
30
+ min_slot_minutes: int
31
+ default_duration_minutes: int
32
+ lead_time_minutes: int
33
+
34
+
35
+ class MemberAvailabilityDetail(CasaModel):
36
+ availability_request: MemberAvailabilitySummary
37
+ slots: list[MemberAvailabilitySlot]
38
+ home_timezone: str
39
+ constraints: MemberAvailabilityConstraints
@@ -0,0 +1,134 @@
1
+ """Models for ``POST /protected/member/bootstrap`` (VERIFIED)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any
7
+
8
+ from pydantic import Field
9
+
10
+ from ._base import CasaModel, epoch_to_dt
11
+ from .enums import HomeStatusField, PaymentMethodTypeField
12
+
13
+
14
+ class UserDeviceRequest(CasaModel):
15
+ device_id: str
16
+ device_name: str
17
+ system_name: str
18
+ system_version: str
19
+ model: str
20
+ language: str
21
+ timezone: str
22
+ app: str
23
+ app_version: str
24
+ app_build: str
25
+ fcm_token: str | None = None
26
+ voip_token: str | None = None
27
+
28
+
29
+ class HomeBadgeCounts(CasaModel):
30
+ computed_at: int
31
+ total: int
32
+ features: dict[str, int] = Field(default_factory=dict)
33
+
34
+ @property
35
+ def computed_at_dt(self) -> datetime | None:
36
+ return epoch_to_dt(self.computed_at)
37
+
38
+
39
+ class PendingAgreement(CasaModel):
40
+ """``MemberSimpleHomeAgreement``. ``content`` is the full agreement text."""
41
+
42
+ id: str
43
+ name: str
44
+ content: str
45
+
46
+
47
+ class AppUpdate(CasaModel):
48
+ force_update_required: bool
49
+ current_build: str
50
+ min_required_build: str
51
+ message: str
52
+ update_url: str | None = None
53
+
54
+
55
+ class MemberWaitlistEntry(CasaModel):
56
+ id: str
57
+ address_street_number: str
58
+ address_street: str
59
+ address_city: str
60
+ address_state: str
61
+ address_postal_code: str
62
+ status: str
63
+ created_at: int
64
+
65
+
66
+ class MemberSimpleHome(CasaModel):
67
+ id: str
68
+ address_street_number: str
69
+ address_street: str
70
+ address_city: str
71
+ address_state: str
72
+ address_postal_code: str
73
+ latitude: float | None = None
74
+ longitude: float | None = None
75
+ timezone: str | None = None
76
+ status: HomeStatusField
77
+ card_surcharge_rate: float = 0.0
78
+ has_payment_method: bool = False
79
+ membership_active: bool = False
80
+ is_multi_user_home: bool = False
81
+ has_pending_post_onboarding: bool = False
82
+ pending_agreements: list[PendingAgreement] = Field(default_factory=list)
83
+ address_subpremise: str | None = None
84
+ home_image_url: str | None = None
85
+ home_image_url_cache_key: str | None = None
86
+ payment_description: str | None = None
87
+ payment_method_type: PaymentMethodTypeField | None = None
88
+ payment_method_last4: str | None = None
89
+ payment_method_brand: str | None = None
90
+ membership_name: str | None = None
91
+ membership_plan: Any | None = None
92
+ membership_amount: str | None = None
93
+ membership_next_renewal: int | None = None
94
+ matterport_model_id: str | None = None
95
+ matterport_model_password: str | None = None
96
+ badge_counts: HomeBadgeCounts | None = None
97
+ # Observed in traffic but absent from the app's Codable model.
98
+ plan_state: str | None = None
99
+ route: str | None = None
100
+ is_primary_member: bool | None = None
101
+ past_due_until: int | None = None
102
+ scheduled_change: Any | None = None
103
+
104
+ @property
105
+ def display_address(self) -> str:
106
+ street = f"{self.address_street_number} {self.address_street}"
107
+ if self.address_subpremise:
108
+ street = f"{street} {self.address_subpremise}"
109
+ return f"{street}, {self.address_city}, {self.address_state} {self.address_postal_code}"
110
+
111
+
112
+ class Bootstrap(CasaModel):
113
+ id: str
114
+ created_at: int
115
+ phone_number: str
116
+ email: str
117
+ first_name: str
118
+ last_name: str
119
+ homes: list[MemberSimpleHome] = Field(default_factory=list)
120
+ waitlist_entries: list[MemberWaitlistEntry] = Field(default_factory=list)
121
+ is_casa_collective: bool = False
122
+ avatar_url: str | None = None
123
+ avatar_url_cache_key: str | None = None
124
+ app_update: AppUpdate | None = None
125
+ # Observed in traffic but absent from the app's Codable model.
126
+ calendar_sync_active: bool | None = None
127
+ calendar_sync_email: str | None = None
128
+ calendar_sync_insufficient_scope: bool | None = None
129
+ calendar_sync_options: list[Any] = Field(default_factory=list)
130
+ user_prefs: list[Any] = Field(default_factory=list)
131
+
132
+ @property
133
+ def created_at_dt(self) -> datetime | None:
134
+ return epoch_to_dt(self.created_at)
@@ -0,0 +1,33 @@
1
+ """Visit calendar models (UNVERIFIED endpoint)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any
7
+
8
+ from pydantic import Field
9
+
10
+ from ._base import CasaModel, epoch_to_dt
11
+
12
+
13
+ class CalendarEvent(CasaModel):
14
+ """``VisitCalendarEvent``. ``kind`` is a Swift enum with associated payloads
15
+ (vendor visit or DTD), so its wire shape is unknown. Kept loose until traffic
16
+ shows it."""
17
+
18
+ id: str
19
+ start_time: int
20
+ end_time: int
21
+ kind: Any = None
22
+
23
+ @property
24
+ def start_dt(self) -> datetime | None:
25
+ return epoch_to_dt(self.start_time)
26
+
27
+ @property
28
+ def end_dt(self) -> datetime | None:
29
+ return epoch_to_dt(self.end_time)
30
+
31
+
32
+ class VisitsCalendarResponse(CasaModel):
33
+ events: list[CalendarEvent] = Field(default_factory=list)
@@ -0,0 +1,73 @@
1
+ """Inventory catalogs and recurring vendors. Vendor list items remain UNVERIFIED."""
2
+
3
+ from pydantic import Field, JsonValue, RootModel
4
+
5
+ from casahome.models._base import CasaModel
6
+ from casahome.models.home import EntityMake
7
+
8
+
9
+ class RoomType(CasaModel):
10
+ id: str
11
+ name: str
12
+
13
+
14
+ class RoomTypesResponse(RootModel[list[RoomType]]):
15
+ """The room type catalog is a bare list."""
16
+
17
+
18
+ class InventoryMakesResponse(CasaModel):
19
+ makes: list[EntityMake]
20
+
21
+
22
+ class InventoryEnumOption(CasaModel):
23
+ """One inventory enum option. VERIFIED populated (live GET, 2026-10)."""
24
+
25
+ id: str
26
+ type: str
27
+ name: str
28
+ symbol_name: str | None = None
29
+ category: str | None = None
30
+ icon_url: str | None = None
31
+
32
+
33
+ class InventoryEnumsResponse(CasaModel):
34
+ """Inventory enum catalog. VERIFIED envelope (live GET, 2026-10)."""
35
+
36
+ enums: list[InventoryEnumOption]
37
+
38
+
39
+ class RecurringVendorCategory(CasaModel):
40
+ id: str
41
+ name: str
42
+ sort_order: int
43
+ primary_color: str
44
+ icon_url: str
45
+ icon_symbol_name: str
46
+ pattern_url: str
47
+ vendor_type_description: str
48
+
49
+
50
+ class RecurringVendorCategoriesResponse(RootModel[list[RecurringVendorCategory]]):
51
+ """Populated category metadata observed."""
52
+
53
+
54
+ class RecurringVendor(CasaModel):
55
+ """Minimal static SimpleDtd fields. Nested events and invoices remain opaque."""
56
+
57
+ id: str | None = None
58
+ home_id: str | None = None
59
+ vendor_id: str | None = None
60
+ vendor_name: str | None = None
61
+ service_category: str | None = None
62
+ dtd_category_id: str | None = None
63
+ rrule: str | None = None
64
+ start_date: str | None = None
65
+ duration_seconds: int | None = None
66
+ next_visit_epoch_seconds: int | None = None
67
+ payment_pending: bool | None = None
68
+ events: list[JsonValue] = Field(default_factory=list)
69
+ invoices: list[JsonValue] = Field(default_factory=list)
70
+
71
+
72
+ class RecurringVendorsResponse(RootModel[list[RecurringVendor]]):
73
+ """Bare list. Empty in the saved response."""
@@ -0,0 +1,29 @@
1
+ """Credits models (UNVERIFIED endpoint)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic import Field
6
+
7
+ from ._base import CasaModel
8
+
9
+
10
+ class CreditHistory(CasaModel):
11
+ id: str
12
+ issued_at: int
13
+ amount: int
14
+ available_amount: int
15
+ expires_at: int | None = None
16
+
17
+
18
+ class ConsumptionHistory(CasaModel):
19
+ id: str
20
+ date: int
21
+ consumed_amount: int
22
+
23
+
24
+ class MemberCredits(CasaModel):
25
+ home_id: str
26
+ total_available: int
27
+ handyman_hourly_rate_cents: int
28
+ credits_history: list[CreditHistory] = Field(default_factory=list)
29
+ consumption_history: list[ConsumptionHistory] = Field(default_factory=list)
@@ -0,0 +1,127 @@
1
+ """Document envelopes observed empty. Item schemas are static and UNVERIFIED."""
2
+
3
+ from pydantic import Field
4
+
5
+ from casahome.models._base import CasaModel
6
+
7
+
8
+ class DocumentOrganization(CasaModel):
9
+ id: str | None = None
10
+ status: str | None = None
11
+ error_code: str | None = None
12
+ can_retry: bool | None = None
13
+ completed_at: int | None = None
14
+
15
+
16
+ class DocumentExtraction(DocumentOrganization):
17
+ pending_count: int | None = None
18
+ pending_structure_count: int | None = None
19
+ accepted_count: int | None = None
20
+ dismissed_count: int | None = None
21
+ already_handled_count: int | None = None
22
+
23
+
24
+ class HomeDocument(CasaModel):
25
+ id: str | None = None
26
+ home_id: str | None = None
27
+ original_filename: str | None = None
28
+ name: str | None = None
29
+ document_type: str | None = None
30
+ document_date: str | None = None
31
+ mimetype: str | None = None
32
+ size_bytes: int | None = None
33
+ uploaded_by_name: str | None = None
34
+ created_at: int | None = None
35
+ updated_at: int | None = None
36
+ organization: DocumentOrganization | None = None
37
+ extraction: DocumentExtraction | None = None
38
+
39
+
40
+ class DocumentsResponse(CasaModel):
41
+ documents: list[HomeDocument]
42
+ next_cursor: str | None = None
43
+
44
+
45
+ class DocumentType(CasaModel):
46
+ key: str
47
+ label: str
48
+ description: str
49
+
50
+
51
+ class DocumentTypesResponse(CasaModel):
52
+ """Populated document policy and upload limits were observed."""
53
+
54
+ types: list[DocumentType]
55
+ max_file_size_bytes: int
56
+ max_files_per_upload: int
57
+ allowed_mimetypes: list[str]
58
+ max_organization_size_bytes: int | None = None
59
+
60
+
61
+ class DocumentCitation(CasaModel):
62
+ page_index: int | None = None
63
+ page_label: str | None = None
64
+ excerpt: str | None = None
65
+
66
+
67
+ class WorkDocumentSource(CasaModel):
68
+ document_id: str | None = None
69
+ name: str | None = None
70
+ available: bool | None = None
71
+ document_date: str | None = None
72
+ citations: list[DocumentCitation] = Field(default_factory=list)
73
+
74
+
75
+ class WorkDocumentSourceResponse(CasaModel):
76
+ source: WorkDocumentSource | None
77
+
78
+
79
+ class DocumentDetailsResponse(CasaModel):
80
+ """UNVERIFIED document detail envelope."""
81
+
82
+ document: HomeDocument
83
+
84
+
85
+ class DocumentSuggestion(CasaModel):
86
+ """Static document suggestion fields."""
87
+
88
+ id: str
89
+ todo_id: str
90
+ title: str
91
+ action: str
92
+ reason: str
93
+ area: str | None = None
94
+ citations: list[DocumentCitation]
95
+ state: str
96
+ work_deleted: bool
97
+ task_id: str | None = None
98
+ request_id: str | None = None
99
+
100
+
101
+ class DocumentSuggestionsResponse(CasaModel):
102
+ """UNVERIFIED suggestions envelope."""
103
+
104
+ suggestions: list[DocumentSuggestion]
105
+ analysis_status: str | None = None
106
+ limitations: list[str]
107
+
108
+
109
+ class DocumentStructureSuggestion(CasaModel):
110
+ """Static structure suggestion metadata."""
111
+
112
+ id: str
113
+ section: str
114
+ title: str
115
+ current_values: list[str]
116
+ current_label: str
117
+ proposed_label: str
118
+ reason: str
119
+ state: str
120
+ changed_since_analysis: bool
121
+ citations: list[DocumentCitation]
122
+
123
+
124
+ class DocumentStructureSuggestionsResponse(CasaModel):
125
+ """UNVERIFIED structure suggestion page."""
126
+
127
+ suggestions: list[DocumentStructureSuggestion]
@@ -0,0 +1,183 @@
1
+ """String enums transcribed from the Casa app binary.
2
+
3
+ Values are the Swift case names. Model fields type these as ``Enum | str`` so an
4
+ unseen value from the server degrades to a plain string instead of failing.
5
+ The Swift ``unknown`` fallback case is included because the app defines it,
6
+ but the server is not expected to send it.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from enum import StrEnum
12
+ from typing import Annotated
13
+
14
+ from pydantic import Field
15
+
16
+
17
+ class MemberSimpleHomeStatus(StrEnum):
18
+ ONBOARDING = "onboarding"
19
+ ACTIVE = "active"
20
+ INACTIVE = "inactive"
21
+ UNKNOWN = "unknown"
22
+
23
+
24
+ class PaymentMethodType(StrEnum):
25
+ CARD = "card"
26
+ US_BANK_ACCOUNT = "us_bank_account"
27
+ UNKNOWN = "unknown"
28
+
29
+
30
+ class RequestType(StrEnum):
31
+ CONCIERGE = "concierge"
32
+ HANDYMAN = "handyman"
33
+ THIRD_PARTY = "third_party"
34
+ UNKNOWN = "unknown"
35
+
36
+
37
+ class RequestStatus(StrEnum):
38
+ PENDING_CONCIERGE = "pending_concierge"
39
+ PENDING_MEMBER = "pending_member"
40
+ PENDING_VENDOR = "pending_vendor"
41
+ COMPLETED = "completed"
42
+ CANCELED = "canceled"
43
+ UNKNOWN = "unknown"
44
+
45
+
46
+ class SenderRole(StrEnum):
47
+ CONCIERGE = "concierge"
48
+ MEMBER = "member"
49
+ VENDOR = "vendor"
50
+ SYSTEM = "system"
51
+ UNKNOWN = "unknown"
52
+
53
+
54
+ class VisitType(StrEnum):
55
+ HANDYMAN = "handyman"
56
+ THIRD_PARTY = "third_party"
57
+ UNKNOWN = "unknown"
58
+
59
+
60
+ class VendorVisitStatus(StrEnum):
61
+ DRAFT = "draft"
62
+ SCHEDULED = "scheduled"
63
+ IN_PROGRESS = "in_progress"
64
+ WRAP_UP = "wrap_up"
65
+ COMPLETED = "completed"
66
+ CANCELED = "canceled"
67
+ UNKNOWN = "unknown"
68
+
69
+
70
+ class TodoStatus(StrEnum):
71
+ PREPARING = "preparing"
72
+ NEEDS_SUPPORT_CONFIRMATION = "needs_support_confirmation"
73
+ NEEDS_INPUT = "needs_input"
74
+ READY_FOR_CONCIERGE = "ready_for_concierge"
75
+ FAILED = "failed"
76
+ MATERIALIZED = "materialized"
77
+ SUGGESTION_CONCIERGE_APPROVAL_PENDING = "suggestion_concierge_approval_pending"
78
+ SUGGESTION_CONCIERGE_APPROVED = "suggestion_concierge_approved"
79
+ SUGGESTION_CONCIERGE_REJECTED = "suggestion_concierge_rejected"
80
+ SUGGESTION_MEMBER_REVIEW_PENDING = "suggestion_member_review_pending"
81
+ SUGGESTION_MEMBER_REJECTED = "suggestion_member_rejected"
82
+ UNKNOWN = "unknown"
83
+
84
+
85
+ class TodoCreatorRole(StrEnum):
86
+ MEMBER = "member"
87
+ CONCIERGE = "concierge"
88
+ VENDOR = "vendor"
89
+ CASA_AI = "casa_ai"
90
+ UNKNOWN = "unknown"
91
+
92
+
93
+ class TodoLaneCardKind(StrEnum):
94
+ TODO = "todo"
95
+ TASK = "task"
96
+ UNKNOWN = "unknown"
97
+
98
+
99
+ class TodoLaneCardState(StrEnum):
100
+ PREPARING = "preparing"
101
+ NEEDS_INPUT = "needs_input"
102
+ READY_FOR_CONCIERGE = "ready_for_concierge"
103
+ FAILED = "failed"
104
+ READY_FOR_SCHEDULING = "ready_for_scheduling"
105
+ UNKNOWN = "unknown"
106
+
107
+
108
+ class PackageDirection(StrEnum):
109
+ INBOUND = "inbound"
110
+ OUTBOUND = "outbound"
111
+ UNKNOWN = "unknown"
112
+
113
+
114
+ class PackagePickupStatus(StrEnum):
115
+ AWAITING_PICKUP = "awaiting_pickup"
116
+ PICKED_UP = "picked_up"
117
+ DROPPED_OFF = "dropped_off"
118
+ MISSED_PICKUP = "missed_pickup"
119
+ UNKNOWN = "unknown"
120
+
121
+
122
+ class WeatherCondition(StrEnum):
123
+ BLIZZARD = "blizzard"
124
+ BLOWING_SNOW = "blowingSnow"
125
+ BREEZY = "breezy"
126
+ CLEAR = "clear"
127
+ CLOUDY = "cloudy"
128
+ DRIZZLE = "drizzle"
129
+ DUST = "dust"
130
+ FOG = "fog"
131
+ FREEZING_DRIZZLE = "freezingDrizzle"
132
+ FREEZING_RAIN = "freezingRain"
133
+ FLURRIES = "flurries"
134
+ FRIGID = "frigid"
135
+ HAIL = "hail"
136
+ HAZE = "haze"
137
+ HEAVY_RAIN = "heavyRain"
138
+ HEAVY_SNOW = "heavySnow"
139
+ HOT = "hot"
140
+ HURRICANE = "hurricane"
141
+ ISOLATED_THUNDERSTORMS = "isolatedThunderstorms"
142
+ MOSTLY_CLEAR = "mostlyClear"
143
+ MOSTLY_CLOUDY = "mostlyCloudy"
144
+ MIXED_RAIN_AND_SLEET = "mixedRainAndSleet"
145
+ MIXED_RAIN_AND_SNOW = "mixedRainAndSnow"
146
+ MIXED_RAINFALL = "mixedRainfall"
147
+ MIXED_SNOW_AND_SLEET = "mixedSnowAndSleet"
148
+ PARTLY_CLOUDY = "partlyCloudy"
149
+ RAIN = "rain"
150
+ SCATTERED_SHOWERS = "scatteredShowers"
151
+ SCATTERED_SNOW_SHOWERS = "scatteredSnowShowers"
152
+ SCATTERED_THUNDERSTORMS = "scatteredThunderstorms"
153
+ SEVERE_THUNDERSTORM = "severeThunderstorm"
154
+ SHOWERS = "showers"
155
+ SLEET = "sleet"
156
+ SMOKE = "smoke"
157
+ SNOW = "snow"
158
+ SNOW_SHOWERS = "snowShowers"
159
+ THUNDERSTORM = "thunderstorm"
160
+ TORNADO = "tornado"
161
+ TROPICAL_STORM = "tropicalStorm"
162
+ WINDY = "windy"
163
+ UNKNOWN = "unknown"
164
+
165
+
166
+ # Lenient field types. ``left_to_right`` makes pydantic try the enum first and
167
+ # fall back to ``str`` for values the app binary didn't know about.
168
+ _LTR = Field(union_mode="left_to_right")
169
+
170
+ HomeStatusField = Annotated[MemberSimpleHomeStatus | str, _LTR]
171
+ PaymentMethodTypeField = Annotated[PaymentMethodType | str, _LTR]
172
+ RequestTypeField = Annotated[RequestType | str, _LTR]
173
+ RequestStatusField = Annotated[RequestStatus | str, _LTR]
174
+ SenderRoleField = Annotated[SenderRole | str, _LTR]
175
+ VisitTypeField = Annotated[VisitType | str, _LTR]
176
+ VendorVisitStatusField = Annotated[VendorVisitStatus | str, _LTR]
177
+ TodoStatusField = Annotated[TodoStatus | str, _LTR]
178
+ TodoCreatorRoleField = Annotated[TodoCreatorRole | str, _LTR]
179
+ TodoLaneCardKindField = Annotated[TodoLaneCardKind | str, _LTR]
180
+ TodoLaneCardStateField = Annotated[TodoLaneCardState | str, _LTR]
181
+ PackageDirectionField = Annotated[PackageDirection | str, _LTR]
182
+ PackagePickupStatusField = Annotated[PackagePickupStatus | str, _LTR]
183
+ WeatherConditionField = Annotated[WeatherCondition | str, _LTR]
@@ -0,0 +1,30 @@
1
+ """Home feed cards, ``/protected/home/{home_id}/feed``.
2
+
3
+ VERIFIED by a live GET (2026-10) with ``app_build=698``: an envelope
4
+ ``cards`` holding populated items. Every card had the keys modeled below.
5
+ ``props`` varies by ``template_id`` (titles, captions, actions, images,
6
+ colors, ``visual_*`` styling, onboarding counts), so it stays a JSON dict.
7
+ ``children`` was empty on every observed card. It's typed recursively on the
8
+ assumption children are cards too, which is UNVERIFIED. Unknown fields ride
9
+ along in ``model_extra``.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from pydantic import Field, JsonValue
15
+
16
+ from casahome.models._base import CasaModel
17
+
18
+
19
+ class HomeFeedCard(CasaModel):
20
+ id: str
21
+ template_id: str
22
+ name: str
23
+ props: dict[str, JsonValue] = Field(default_factory=dict)
24
+ show_starts_at_minutes_after_onboarding: int
25
+ show_ends_at_minutes_after_onboarding: int | None = None
26
+ children: list[HomeFeedCard] = Field(default_factory=list)
27
+
28
+
29
+ class HomeFeedResponse(CasaModel):
30
+ cards: list[HomeFeedCard]
@@ -0,0 +1,45 @@
1
+ """Help center bundle models, inferred from the app.
2
+
3
+ A populated ``member_ios`` bundle parsed with these models in live traffic
4
+ (2026-10). Because ``populate_by_name``
5
+ accepts either spelling, that doesn't confirm the camelCase aliases are the
6
+ wire keys. No other app ID, article-view tracking or feedback is inferred.
7
+ """
8
+
9
+ from pydantic import Field
10
+
11
+ from casahome.models._base import CasaModel
12
+
13
+
14
+ class HelpCenterTopic(CasaModel):
15
+ id: str
16
+ slug: str
17
+ name: str
18
+ blurb: str
19
+ system_image: str = Field(alias="systemImage")
20
+ hero_image_url: str | None = Field(default=None, alias="heroImageURL")
21
+ sort_order: int = Field(alias="sortOrder")
22
+
23
+
24
+ class HelpCenterArticle(CasaModel):
25
+ id: str
26
+ topic_id: str = Field(alias="topicId")
27
+ slug: str
28
+ sort_order: int = Field(alias="sortOrder")
29
+ is_top_question: bool = Field(alias="isTopQuestion")
30
+ is_featured: bool = Field(alias="isFeatured")
31
+ article_version_id: str = Field(alias="articleVersionId")
32
+ version_number: int = Field(alias="versionNumber")
33
+ title: str
34
+ summary: str
35
+ body_markdown: str = Field(alias="bodyMarkdown")
36
+
37
+
38
+ class HelpCenterBundle(CasaModel):
39
+ app: str
40
+ topics: list[HelpCenterTopic]
41
+ articles: list[HelpCenterArticle]
42
+
43
+
44
+ class HelpCenterBundleResponse(CasaModel):
45
+ data: HelpCenterBundle