diff --git a/codegen/layouts/partials/resource-dataclass.hbs b/codegen/layouts/partials/resource-dataclass.hbs index e52750c6..d9f4966e 100644 --- a/codegen/layouts/partials/resource-dataclass.hbs +++ b/codegen/layouts/partials/resource-dataclass.hbs @@ -33,6 +33,6 @@ {{/unless}} {{memberIndent}} return cls( {{#each properties}} -{{../memberIndent}} {{pythonIdentifier name}}={{#if isObject}}cls.{{nestedClassName}}.from_dict(d.get("{{name}}")) if d.get("{{name}}") is not None else None{{else}}{{#if isDiscriminatedObjectList}}[_from_discriminated_dict(i, cls._{{nestedClassName}}Variants, "{{discriminator}}") for i in d.get("{{name}}") or []]{{else}}{{#if isObjectList}}[cls.{{nestedClassName}}.from_dict(i) for i in d.get("{{name}}") or []]{{else}}{{#if isDictParam}}DeepAttrDict({{/if}}d.get("{{name}}", None){{#if isDictParam}}){{/if}}{{/if}}{{/if}}{{/if}}, +{{../memberIndent}} {{pythonIdentifier name}}={{#if isRequiredObject}}cls.{{nestedClassName}}.from_dict(d.get("{{name}}") or {}){{else}}{{#if isObject}}cls.{{nestedClassName}}.from_dict(d.get("{{name}}")) if d.get("{{name}}") is not None else None{{else}}{{#if isDiscriminatedObjectList}}[_from_discriminated_dict(i, cls._{{nestedClassName}}Variants, "{{discriminator}}") for i in d.get("{{name}}") or []]{{else}}{{#if isObjectList}}[cls.{{nestedClassName}}.from_dict(i) for i in d.get("{{name}}") or []]{{else}}{{#if isDictParam}}DeepAttrDict({{/if}}d.get("{{name}}", None){{#if isDictParam}}){{/if}}{{/if}}{{/if}}{{/if}}{{/if}}, {{/each}} {{memberIndent}} ) diff --git a/codegen/layouts/resource.hbs b/codegen/layouts/resource.hbs index 72c9250b..dd7306cd 100644 --- a/codegen/layouts/resource.hbs +++ b/codegen/layouts/resource.hbs @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Literal, Optional, Union{{#if union}}, cast{{/if}} +from typing import Any, Dict, List, Literal, Optional, {{#if union.secondaryDiscriminator}}Tuple, {{/if}}Union{{#if union}}, cast{{/if}} from dataclasses import dataclass from ..deep_attr_dict import DeepAttrDict from ..resource_mapping import ResourceMapping @@ -20,22 +20,26 @@ def _from_discriminated_dict( {{#if union}} {{union.className}} = Union[{{#each union.variants}}{{className}}{{#unless @last}}, {{/unless}}{{/each}}] -{{union.variantsName}}: Dict[str, Any] = { +{{#each union.aliases}} +{{className}} = Union[{{#each variantClassNames}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}] + +{{/each}} +{{union.variantsName}}: Dict[{{#if union.secondaryDiscriminator}}Tuple[str, str]{{else}}str{{/if}}, Any] = { {{#each union.variants}} {{#each values}} - {{pythonString this}}: {{../className}}, + {{#if @root.union.secondaryDiscriminator}}({{pythonString this}}, {{pythonString ../secondaryValue}}){{else}}{{pythonString this}}{{/if}}: {{../className}}, {{/each}} {{/each}} } def {{union.fromDictName}}(d: Any) -> {{union.className}}: - """Deserialize a known {{union.discriminator}} variant. + """Deserialize a known {{union.discriminator}}{{#if union.secondaryDiscriminator}} and {{union.secondaryDiscriminator}}{{/if}} variant. Unknown discriminator values return ``DeepAttrDict`` so payloads from a newer API remain readable. The static return type covers known variants. """ - variant = {{union.variantsName}}.get(d.get("{{union.discriminator}}")) + variant = {{union.variantsName}}.get({{#if union.secondaryDiscriminator}}(d.get("{{union.discriminator}}"), d.get("{{union.secondaryDiscriminator}}")){{else}}d.get("{{union.discriminator}}"){{/if}}) if variant is None: return cast({{union.className}}, DeepAttrDict(d)) return variant.from_dict(d) diff --git a/codegen/lib/layouts/resources.ts b/codegen/lib/layouts/resources.ts index 53ffbcb1..f1eb4968 100644 --- a/codegen/lib/layouts/resources.ts +++ b/codegen/lib/layouts/resources.ts @@ -2,7 +2,12 @@ // Each blueprint resource, along with events, action attempts, and pagination, // becomes a dataclass in its own module, re-exported from seam/resources/__init__.py. -import type { Blueprint, Property } from '@seamapi/blueprint' +import type { + ActionAttemptStatus, + Blueprint, + EnumProperty, + Property, +} from '@seamapi/blueprint' import { pascalCase, snakeCase } from 'change-case' import { convertCustomResourceName } from '../custom-resource-name-conversions.js' @@ -38,9 +43,15 @@ interface ResourceClassLayoutContext { interface DiscriminatedUnionLayoutContext { className: string discriminator: string + secondaryDiscriminator?: string fromDictName: string variantsName: string - variants: Array<{ className: string; values: string[] }> + variants: Array<{ + className: string + values: string[] + secondaryValue?: string + }> + aliases?: Array<{ className: string; variantClassNames: string[] }> } interface ResourcePropertyLayoutContext { @@ -52,6 +63,7 @@ interface ResourcePropertyLayoutContext { nestedClassName: string isDictParam: boolean isObject: boolean + isRequiredObject: boolean isObjectList: boolean isDiscriminatedObjectList: boolean discriminator: string @@ -248,6 +260,17 @@ const reservedClassNames = new Set([ 'dataclass', ]) +type StatusAnnotatedProperty = Property & { + renderAsNone?: boolean + presentForStatus?: boolean +} + +const isRenderedAsNone = (property: Property): boolean => + (property as StatusAnnotatedProperty).renderAsNone === true + +const isPresentForStatus = (property: Property): boolean => + (property as StatusAnnotatedProperty).presentForStatus === true + const getNestedProperties = (property: Property): Property[] | undefined => { if (property.format === 'object') return property.properties if (property.format === 'list' && property.itemFormat === 'object') { @@ -296,6 +319,23 @@ const buildClass = ( const takenClassNames = new Set() const properties = classProperties.map((property) => { + if (isRenderedAsNone(property)) { + return { + name: property.name, + description: property.description, + isDeprecated: property.isDeprecated, + deprecationMessage: property.deprecationMessage, + type: 'None', + nestedClassName: '', + isDictParam: false, + isObject: false, + isRequiredObject: false, + isObjectList: false, + isDiscriminatedObjectList: false, + discriminator: '', + } + } + const nestedProperties = getNestedProperties(property) const nestedPath = `${path}.${property.name}` const isDiscriminatedObjectList = @@ -379,12 +419,16 @@ const buildClass = ( } const isObject = nestedClassName != null && property.format === 'object' - // A nested object is read as None whenever the payload omits it, and the - // schema is not a reliable guide to when that happens: an action attempt - // documents both error and result as required, yet a pending one carries - // neither. Constructing them unconditionally would fail on those payloads, - // so from_dict keeps its None fallback and the field stays Optional. - const type = mapPropertyToPythonType(property, nestedClassName, isObject) + const isRequiredObject = + isObject && + isPresentForStatus(property) && + !property.isOptional && + !property.isNullable + const type = mapPropertyToPythonType( + property, + nestedClassName, + isObject && !isRequiredObject, + ) const requiredType = mapRequiredPropertyToPythonType( property, nestedClassName, @@ -400,6 +444,7 @@ const buildClass = ( nestedClassName: nestedClassName ?? '', isDictParam: requiredType.startsWith('Dict'), isObject, + isRequiredObject, isObjectList: !isDiscriminatedObjectList && nestedClassName != null && @@ -427,41 +472,108 @@ const hasDiscriminatedLists = ( resourceClass.nestedUnions.length > 0 || resourceClass.nestedClasses.some(hasDiscriminatedLists) +interface UnionVariant { + value: string + secondaryValue?: string + description: string + properties: Property[] + isDeprecated: boolean + deprecationMessage: string +} + +const buildUnionAliases = ( + variants: Array<{ className: string; groupValue: string | undefined }>, + suffix: string, +): Array<{ className: string; variantClassNames: string[] }> => { + const groups = new Map() + for (const { className, groupValue } of variants) { + if (groupValue == null) continue + const group = groups.get(groupValue) + if (group == null) { + groups.set(groupValue, [className]) + } else { + group.push(className) + } + } + return [...groups.entries()].map(([value, variantClassNames]) => ({ + className: `${pythonClassName(value)}${suffix}`, + variantClassNames, + })) +} + const buildUnionResource = ( className: string, discriminator: string, fromDictName: string, - variants: Array<{ - value: string - description: string - properties: Property[] - isDeprecated: boolean - deprecationMessage: string - }>, + variants: UnionVariant[], isDeprecated: boolean, deprecationMessage: string, + secondaryDiscriminator?: string, ): ResourceLayoutContext => { const suffix = className === 'SeamEvent' ? 'Event' : 'ActionAttempt' - const classes = variants.map((variant) => ({ - ...buildClass( - `${pythonClassName(variant.value)}${suffix}`, - variant.description, - variant.properties, - `${snakeCase(className)}.${variant.value}`, - rootIndentation, - ), - isDeprecated: variant.isDeprecated, - deprecationMessage: variant.deprecationMessage, - })) + const classes = variants.map((variant) => { + const secondaryName = + variant.secondaryValue == null + ? '' + : pythonClassName(variant.secondaryValue) + const secondaryPath = + variant.secondaryValue == null ? '' : `.${variant.secondaryValue}` + return { + ...buildClass( + `${pythonClassName(variant.value)}${secondaryName}${suffix}`, + variant.description, + variant.properties, + `${snakeCase(className)}.${variant.value}${secondaryPath}`, + rootIndentation, + ), + isDeprecated: variant.isDeprecated, + deprecationMessage: variant.deprecationMessage, + } + }) + + const aliases = + secondaryDiscriminator == null + ? [] + : [ + ...buildUnionAliases( + classes.map(({ className: name }, index) => ({ + className: name, + groupValue: variants[index]?.value, + })), + suffix, + ), + ...buildUnionAliases( + classes.map(({ className: name }, index) => ({ + className: name, + groupValue: variants[index]?.secondaryValue, + })), + suffix, + ), + ] + const classNames = new Set(classes.map(({ className: name }) => name)) + for (const alias of aliases) { + if (classNames.has(alias.className) || alias.className === className) { + throw new Error( + `The union alias ${alias.className} collides with a generated class name.`, + ) + } + } + const union = { className, discriminator, + ...(secondaryDiscriminator == null ? {} : { secondaryDiscriminator }), fromDictName, variantsName: `_${snakeCase(className).toUpperCase()}_VARIANTS`, - variants: classes.map((variantClass, index) => ({ - className: variantClass.className, - values: [variants[index]?.value ?? ''], - })), + variants: classes.map((variantClass, index) => { + const secondaryValue = variants[index]?.secondaryValue + return { + className: variantClass.className, + values: [variants[index]?.value ?? ''], + ...(secondaryValue == null ? {} : { secondaryValue }), + } + }), + aliases, } return { @@ -474,12 +586,60 @@ const buildUnionResource = ( hasDiscriminatedLists: classes.some(hasDiscriminatedLists), exports: [ ...classes.map(({ className: name }) => name), + ...aliases.map(({ className: name }) => name), className, fromDictName, ], } } +const expandActionAttemptByStatus = ( + attempt: Blueprint['actionAttempts'][number], +): UnionVariant[] => { + const statusProperty = attempt.properties.find( + (property): property is EnumProperty => + property.name === 'status' && property.format === 'enum', + ) + if (statusProperty == null || statusProperty.values.length === 0) { + throw new Error( + `The ${attempt.actionAttemptType} action attempt must have a status enum property to expand into per-status variants.`, + ) + } + + return statusProperty.values.map(({ name: status }) => ({ + value: attempt.actionAttemptType, + secondaryValue: status, + description: attempt.description, + isDeprecated: attempt.isDeprecated, + deprecationMessage: attempt.deprecationMessage, + properties: attempt.properties.map((property): Property => { + if (property === statusProperty) { + return { + ...statusProperty, + values: statusProperty.values.filter( + (value) => value.name === status, + ), + } + } + const { actionAttemptStatuses } = property + if (actionAttemptStatuses == null) return property + if (actionAttemptStatuses.includes(status as ActionAttemptStatus)) { + const presentProperty: StatusAnnotatedProperty = { + ...property, + presentForStatus: true, + } + return presentProperty + } + const noneProperty: StatusAnnotatedProperty = { + ...property, + isNullable: false, + renderAsNone: true, + } + return noneProperty + }), + })) +} + export const getResourceLayoutContexts = ( blueprint: Blueprint, ): ResourceLayoutContext[] => { @@ -568,15 +728,10 @@ export const getResourceLayoutContexts = ( 'ActionAttempt', 'action_type', 'action_attempt_from_dict', - blueprint.actionAttempts.map((attempt) => ({ - value: attempt.actionAttemptType, - description: attempt.description, - properties: attempt.properties, - isDeprecated: attempt.isDeprecated, - deprecationMessage: attempt.deprecationMessage, - })), + blueprint.actionAttempts.flatMap(expandActionAttemptByStatus), actionAttemptModel?.isDeprecated ?? false, actionAttemptModel?.deprecationMessage ?? '', + 'status', ), ) diff --git a/package-lock.json b/package-lock.json index 71372b48..fa376186 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "@seamapi/python", "devDependencies": { - "@seamapi/blueprint": "^1.9.1", + "@seamapi/blueprint": "^1.10.0", "@seamapi/fake-seam-connect": "2.0.5", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1047.0", @@ -787,9 +787,9 @@ "license": "MIT" }, "node_modules/@seamapi/blueprint": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.9.1.tgz", - "integrity": "sha512-A9H3dZE9f+ZEEnOAlMl5jSlL5F/RIKkjOF8NDFbnuahAiu/trDz/RzHOGhbQd6sd0RErIyx3eG1p4HCOJDKDCA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@seamapi/blueprint/-/blueprint-1.10.0.tgz", + "integrity": "sha512-XyP6zvbhv5naWEa9T9utNLi3FVVmvUB1Htih/IjUqS3Uz0FrIIBWy7Myk5+s4uylEt+wTdtSBaA1fGOF/6ILmA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index c42fcef3..ec8a307c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "packageManager": "npm@11.19.0", "devDependencies": { - "@seamapi/blueprint": "^1.9.1", + "@seamapi/blueprint": "^1.10.0", "@seamapi/fake-seam-connect": "2.0.5", "@seamapi/smith": "^1.1.0", "@seamapi/types": "1.1047.0", diff --git a/seam/exceptions.py b/seam/exceptions.py index afdec1e8..ef9fbeb1 100644 --- a/seam/exceptions.py +++ b/seam/exceptions.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from typing import Any, Dict, List, Optional -from .resources import ActionAttempt +from .resources import ActionAttempt, ErrorActionAttempt, PendingActionAttempt @dataclass(frozen=True) @@ -156,10 +156,10 @@ class SeamActionAttemptFailedError(SeamActionAttemptError): :vartype code: str """ - def __init__(self, action_attempt: ActionAttempt): + def __init__(self, action_attempt: ErrorActionAttempt): """ - :param action_attempt: The ActionAttempt object associated with this error - :type action_attempt: ActionAttempt + :param action_attempt: The failed ActionAttempt object associated with this error + :type action_attempt: ErrorActionAttempt """ # A failed action attempt carries an error, but reading through it @@ -186,10 +186,10 @@ class SeamActionAttemptTimeoutError(SeamActionAttemptError): :vartype name: str """ - def __init__(self, action_attempt: ActionAttempt, timeout: float): + def __init__(self, action_attempt: PendingActionAttempt, timeout: float): """ - :param action_attempt: The ActionAttempt object associated with this error - :type action_attempt: ActionAttempt + :param action_attempt: The still-pending ActionAttempt object associated with this error + :type action_attempt: PendingActionAttempt :param timeout: The timeout duration in seconds :type timeout: float """ diff --git a/seam/modules/action_attempts.py b/seam/modules/action_attempts.py index e87b0b59..461517dd 100644 --- a/seam/modules/action_attempts.py +++ b/seam/modules/action_attempts.py @@ -4,7 +4,7 @@ from ..client import AsyncSeamHttpClient, SeamHttpClient from ..exceptions import SeamActionAttemptFailedError, SeamActionAttemptTimeoutError -from ..resources import ActionAttempt, action_attempt_from_dict +from ..resources import ActionAttempt, SuccessActionAttempt, action_attempt_from_dict TIMEOUT = 5.0 POLLING_INTERVAL = 0.5 @@ -24,7 +24,7 @@ def poll_until_ready( action_attempt_id: str, timeout: float = TIMEOUT, polling_interval: float = POLLING_INTERVAL, -) -> ActionAttempt: +) -> SuccessActionAttempt: time_waiting = 0.0 action_attempt = get_action_attempt(client, action_attempt_id) @@ -84,7 +84,7 @@ async def poll_until_ready_async( action_attempt_id: str, timeout: float = TIMEOUT, polling_interval: float = POLLING_INTERVAL, -) -> ActionAttempt: +) -> SuccessActionAttempt: time_waiting = 0.0 action_attempt = await get_action_attempt_async(client, action_attempt_id) diff --git a/seam/resources/__init__.py b/seam/resources/__init__.py index a981676b..14aac489 100644 --- a/seam/resources/__init__.py +++ b/seam/resources/__init__.py @@ -8,6 +8,69 @@ from .acs_system import AcsSystem from .acs_user import AcsUser from .action_attempt import ( + LockDoorSuccessActionAttempt, + LockDoorPendingActionAttempt, + LockDoorErrorActionAttempt, + UnlockDoorSuccessActionAttempt, + UnlockDoorPendingActionAttempt, + UnlockDoorErrorActionAttempt, + ScanCredentialSuccessActionAttempt, + ScanCredentialPendingActionAttempt, + ScanCredentialErrorActionAttempt, + EncodeCredentialSuccessActionAttempt, + EncodeCredentialPendingActionAttempt, + EncodeCredentialErrorActionAttempt, + ScanToAssignCredentialSuccessActionAttempt, + ScanToAssignCredentialPendingActionAttempt, + ScanToAssignCredentialErrorActionAttempt, + AssignCredentialSuccessActionAttempt, + AssignCredentialPendingActionAttempt, + AssignCredentialErrorActionAttempt, + ResetSandboxWorkspaceSuccessActionAttempt, + ResetSandboxWorkspacePendingActionAttempt, + ResetSandboxWorkspaceErrorActionAttempt, + SetFanModeSuccessActionAttempt, + SetFanModePendingActionAttempt, + SetFanModeErrorActionAttempt, + SetHvacModeSuccessActionAttempt, + SetHvacModePendingActionAttempt, + SetHvacModeErrorActionAttempt, + ActivateClimatePresetSuccessActionAttempt, + ActivateClimatePresetPendingActionAttempt, + ActivateClimatePresetErrorActionAttempt, + SimulateKeypadCodeEntrySuccessActionAttempt, + SimulateKeypadCodeEntryPendingActionAttempt, + SimulateKeypadCodeEntryErrorActionAttempt, + SimulateManualLockViaKeypadSuccessActionAttempt, + SimulateManualLockViaKeypadPendingActionAttempt, + SimulateManualLockViaKeypadErrorActionAttempt, + PushThermostatProgramsSuccessActionAttempt, + PushThermostatProgramsPendingActionAttempt, + PushThermostatProgramsErrorActionAttempt, + ConfigureAutoLockSuccessActionAttempt, + ConfigureAutoLockPendingActionAttempt, + ConfigureAutoLockErrorActionAttempt, + SyncAccessCodesSuccessActionAttempt, + SyncAccessCodesPendingActionAttempt, + SyncAccessCodesErrorActionAttempt, + CreateAccessCodeSuccessActionAttempt, + CreateAccessCodePendingActionAttempt, + CreateAccessCodeErrorActionAttempt, + DeleteAccessCodeSuccessActionAttempt, + DeleteAccessCodePendingActionAttempt, + DeleteAccessCodeErrorActionAttempt, + UpdateAccessCodeSuccessActionAttempt, + UpdateAccessCodePendingActionAttempt, + UpdateAccessCodeErrorActionAttempt, + CreateNoiseThresholdSuccessActionAttempt, + CreateNoiseThresholdPendingActionAttempt, + CreateNoiseThresholdErrorActionAttempt, + DeleteNoiseThresholdSuccessActionAttempt, + DeleteNoiseThresholdPendingActionAttempt, + DeleteNoiseThresholdErrorActionAttempt, + UpdateNoiseThresholdSuccessActionAttempt, + UpdateNoiseThresholdPendingActionAttempt, + UpdateNoiseThresholdErrorActionAttempt, LockDoorActionAttempt, UnlockDoorActionAttempt, ScanCredentialActionAttempt, @@ -29,6 +92,9 @@ CreateNoiseThresholdActionAttempt, DeleteNoiseThresholdActionAttempt, UpdateNoiseThresholdActionAttempt, + SuccessActionAttempt, + PendingActionAttempt, + ErrorActionAttempt, ActionAttempt, action_attempt_from_dict, ) diff --git a/seam/resources/action_attempt.py b/seam/resources/action_attempt.py index 4a6cc6ed..162d4492 100644 --- a/seam/resources/action_attempt.py +++ b/seam/resources/action_attempt.py @@ -1,11 +1,11 @@ -from typing import Any, Dict, List, Literal, Optional, Union, cast +from typing import Any, Dict, List, Literal, Optional, Tuple, Union, cast from dataclasses import dataclass from ..deep_attr_dict import DeepAttrDict from ..resource_mapping import ResourceMapping @dataclass -class LockDoorActionAttempt: +class LockDoorSuccessActionAttempt: """Locking a door is pending. :ivar action_attempt_id: ID of the action attempt. @@ -18,24 +18,6 @@ class LockDoorActionAttempt: :ivar status:""" - @dataclass - class Error(ResourceMapping): - """Error associated with the action. - - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar type: Type of the error.""" - - message: str - type: str - - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - type=d.get("type", None), - ) - @dataclass class Result(ResourceMapping): """Result of the action. @@ -53,36 +35,59 @@ def from_dict(cls, d: Any): action_attempt_id: str action_type: Literal["LOCK_DOOR"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class UnlockDoorActionAttempt: - """Unlocking a door is pending. +class LockDoorPendingActionAttempt: + """Locking a door is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of unlocking a door. + :ivar action_type: Action attempt to track the status of locking a door. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["LOCK_DOOR"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class LockDoorErrorActionAttempt: + """Locking a door is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of locking a door. :ivar error: Error associated with the action. @@ -108,6 +113,37 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) + action_attempt_id: str + action_type: Literal["LOCK_DOOR"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class UnlockDoorSuccessActionAttempt: + """Unlocking a door is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of unlocking a door. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + @dataclass class Result(ResourceMapping): """Result of the action. @@ -125,61 +161,76 @@ def from_dict(cls, d: Any): action_attempt_id: str action_type: Literal["UNLOCK_DOOR"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class ScanCredentialActionAttempt: - """Reading credential data from the physical encoder is pending. +class UnlockDoorPendingActionAttempt: + """Unlocking a door is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of scanning a credential. + :ivar action_type: Action attempt to track the status of unlocking a door. - :ivar error: + :ivar error: Error associated with the action. - :ivar result: Result of scanning a card. If the attempt was successful, includes a snapshot of credential data read from the physical encoder, the corresponding data stored on Seam and the access system, and any associated warnings. + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["UNLOCK_DOOR"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class UnlockDoorErrorActionAttempt: + """Unlocking a door is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of unlocking a door. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. :ivar status:""" @dataclass class Error(ResourceMapping): - """ + """Error associated with the action. :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar type: Error type to indicate that the Seam Bridge is disconnected or cannot reach the access control system. - """ + :ivar type: Type of the error.""" message: str - type: Literal[ - "uncategorized_error", - "action_attempt_expired", - "no_credential_on_encoder", - "encoder_not_online", - "encoder_communication_timeout", - "bridge_disconnected", - ] + type: str @classmethod def from_dict(cls, d: Any): @@ -188,6 +239,37 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) + action_attempt_id: str + action_type: Literal["UNLOCK_DOOR"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ScanCredentialSuccessActionAttempt: + """Reading credential data from the physical encoder is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of scanning a credential. + + :ivar error: + + :ivar result: Result of scanning a card. If the attempt was successful, includes a snapshot of credential data read from the physical encoder, the corresponding data stored on Seam and the access system, and any associated warnings. + + :ivar status:""" + @dataclass class Result(ResourceMapping): """Result of scanning a card. If the attempt was successful, includes a snapshot of credential data read from the physical encoder, the corresponding data stored on Seam and the access system, and any associated warnings. @@ -668,40 +750,63 @@ def from_dict(cls, d: Any): action_attempt_id: str action_type: Literal["SCAN_CREDENTIAL"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class EncodeCredentialActionAttempt: - """Encoding credential data from the physical encoder onto a card is pending. +class ScanCredentialPendingActionAttempt: + """Reading credential data from the physical encoder is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of encoding credential data from the physical encoder onto a card. + :ivar action_type: Action attempt to track the status of scanning a credential. :ivar error: - :ivar result: Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. + :ivar result: Result of scanning a card. If the attempt was successful, includes a snapshot of credential data read from the physical encoder, the corresponding data stored on Seam and the access system, and any associated warnings. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["SCAN_CREDENTIAL"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ScanCredentialErrorActionAttempt: + """Reading credential data from the physical encoder is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of scanning a credential. + + :ivar error: + + :ivar result: Result of scanning a card. If the attempt was successful, includes a snapshot of credential data read from the physical encoder, the corresponding data stored on Seam and the access system, and any associated warnings. :ivar status:""" @@ -711,7 +816,7 @@ class Error(ResourceMapping): :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar type: Error type to indicate that the credential was deleted and can no longer be encoded. + :ivar type: Error type to indicate that the Seam Bridge is disconnected or cannot reach the access control system. """ message: str @@ -719,13 +824,9 @@ class Error(ResourceMapping): "uncategorized_error", "action_attempt_expired", "no_credential_on_encoder", - "incompatible_card_format", - "credential_cannot_be_reissued", "encoder_not_online", "encoder_communication_timeout", "bridge_disconnected", - "encoding_interrupted", - "credential_deleted", ] @classmethod @@ -735,6 +836,37 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) + action_attempt_id: str + action_type: Literal["SCAN_CREDENTIAL"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class EncodeCredentialSuccessActionAttempt: + """Encoding credential data from the physical encoder onto a card is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of encoding credential data from the physical encoder onto a card. + + :ivar error: + + :ivar result: Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. + + :ivar status:""" + @dataclass class Result(ResourceMapping): """Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. @@ -1056,40 +1188,63 @@ def from_dict(cls, d: Any): action_attempt_id: str action_type: Literal["ENCODE_CREDENTIAL"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class ScanToAssignCredentialActionAttempt: - """Scanning a physical card and assigning the credential is pending. +class EncodeCredentialPendingActionAttempt: + """Encoding credential data from the physical encoder onto a card is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of scanning a physical card and assigning the credential to an ACS user. + :ivar action_type: Action attempt to track the status of encoding credential data from the physical encoder onto a card. :ivar error: - :ivar result: Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. + :ivar result: Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["ENCODE_CREDENTIAL"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class EncodeCredentialErrorActionAttempt: + """Encoding credential data from the physical encoder onto a card is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of encoding credential data from the physical encoder onto a card. + + :ivar error: + + :ivar result: Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. :ivar status:""" @@ -1099,12 +1254,21 @@ class Error(ResourceMapping): :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar type: Error type to indicate that there is no credential on the encoder. + :ivar type: Error type to indicate that the credential was deleted and can no longer be encoded. """ message: str type: Literal[ - "uncategorized_error", "action_attempt_expired", "no_credential_on_encoder" + "uncategorized_error", + "action_attempt_expired", + "no_credential_on_encoder", + "incompatible_card_format", + "credential_cannot_be_reissued", + "encoder_not_online", + "encoder_communication_timeout", + "bridge_disconnected", + "encoding_interrupted", + "credential_deleted", ] @classmethod @@ -1114,11 +1278,42 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) - @dataclass - class Result(ResourceMapping): - """Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. + action_attempt_id: str + action_type: Literal["ENCODE_CREDENTIAL"] + error: Error + result: None + status: Literal["error"] - :ivar access_method: Access method for the `credential `_. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ScanToAssignCredentialSuccessActionAttempt: + """Scanning a physical card and assigning the credential is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of scanning a physical card and assigning the credential to an ACS user. + + :ivar error: + + :ivar result: Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. + + :ivar access_method: Access method for the `credential `_. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. :ivar acs_credential_id: ID of the `credential `_. @@ -1435,40 +1630,63 @@ def from_dict(cls, d: Any): action_attempt_id: str action_type: Literal["SCAN_TO_ASSIGN_CREDENTIAL"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class AssignCredentialActionAttempt: - """Assigning a credential to an access method is pending. +class ScanToAssignCredentialPendingActionAttempt: + """Scanning a physical card and assigning the credential is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of assigning a pre-registered card credential to an access method. + :ivar action_type: Action attempt to track the status of scanning a physical card and assigning the credential to an ACS user. :ivar error: - :ivar result: Result of assigning a credential. If successful, includes the updated access method with the assigned credential. + :ivar result: Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["SCAN_TO_ASSIGN_CREDENTIAL"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ScanToAssignCredentialErrorActionAttempt: + """Scanning a physical card and assigning the credential is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of scanning a physical card and assigning the credential to an ACS user. + + :ivar error: + + :ivar result: Result of a scan to assign attempt. If the attempt was successful, includes the credential data that was scanned and assigned. :ivar status:""" @@ -1478,11 +1696,12 @@ class Error(ResourceMapping): :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar type: Error type to indicate that no matching credential was found.""" + :ivar type: Error type to indicate that there is no credential on the encoder. + """ message: str type: Literal[ - "uncategorized_error", "action_attempt_expired", "credential_not_found" + "uncategorized_error", "action_attempt_expired", "no_credential_on_encoder" ] @classmethod @@ -1492,6 +1711,37 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) + action_attempt_id: str + action_type: Literal["SCAN_TO_ASSIGN_CREDENTIAL"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class AssignCredentialSuccessActionAttempt: + """Assigning a credential to an access method is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of assigning a pre-registered card credential to an access method. + + :ivar error: + + :ivar result: Result of assigning a credential. If successful, includes the updated access method with the assigned credential. + + :ivar status:""" + @dataclass class Result(ResourceMapping): """Result of assigning a credential. If successful, includes the updated access method with the assigned credential. @@ -1715,119 +1965,78 @@ def from_dict(cls, d: Any): action_attempt_id: str action_type: Literal["ASSIGN_CREDENTIAL"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class ResetSandboxWorkspaceActionAttempt: - """Resetting a sandbox workspace is pending. +class AssignCredentialPendingActionAttempt: + """Assigning a credential to an access method is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of resetting a sandbox workspace. + :ivar action_type: Action attempt to track the status of assigning a pre-registered card credential to an access method. - :ivar error: Error associated with the action. + :ivar error: - :ivar result: Result of the action. + :ivar result: Result of assigning a credential. If successful, includes the updated access method with the assigned credential. :ivar status:""" - @dataclass - class Error(ResourceMapping): - """Error associated with the action. - - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar type: Type of the error.""" - - message: str - type: str - - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - type=d.get("type", None), - ) - - @dataclass - class Result(ResourceMapping): - """Result of the action.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - action_attempt_id: str - action_type: Literal["RESET_SANDBOX_WORKSPACE"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["ASSIGN_CREDENTIAL"] + error: None + result: None + status: Literal["pending"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class SetFanModeActionAttempt: - """Setting the fan mode is pending. +class AssignCredentialErrorActionAttempt: + """Assigning a credential to an access method is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of setting the fan mode on a thermostat. + :ivar action_type: Action attempt to track the status of assigning a pre-registered card credential to an access method. - :ivar error: Error associated with the action. + :ivar error: - :ivar result: Result of the action. + :ivar result: Result of assigning a credential. If successful, includes the updated access method with the assigned credential. :ivar status:""" @dataclass class Error(ResourceMapping): - """Error associated with the action. + """ :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar type: Type of the error.""" + :ivar type: Error type to indicate that no matching credential was found.""" message: str - type: str + type: Literal[ + "uncategorized_error", "action_attempt_expired", "credential_not_found" + ] @classmethod def from_dict(cls, d: Any): @@ -1836,47 +2045,30 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) - @dataclass - class Result(ResourceMapping): - """Result of the action.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - action_attempt_id: str - action_type: Literal["SET_FAN_MODE"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["ASSIGN_CREDENTIAL"] + error: Error + result: None + status: Literal["error"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class SetHvacModeActionAttempt: - """Setting the HVAC mode is pending. +class ResetSandboxWorkspaceSuccessActionAttempt: + """Resetting a sandbox workspace is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of setting the HVAC mode on a thermostat. + :ivar action_type: Action attempt to track the status of resetting a sandbox workspace. :ivar error: Error associated with the action. @@ -1884,24 +2076,6 @@ class SetHvacModeActionAttempt: :ivar status:""" - @dataclass - class Error(ResourceMapping): - """Error associated with the action. - - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar type: Type of the error.""" - - message: str - type: str - - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - type=d.get("type", None), - ) - @dataclass class Result(ResourceMapping): """Result of the action.""" @@ -1912,37 +2086,29 @@ def from_dict(cls, d: Any): return cls() action_attempt_id: str - action_type: Literal["SET_HVAC_MODE"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["RESET_SANDBOX_WORKSPACE"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class ActivateClimatePresetActionAttempt: - """Activating a climate preset is pending. +class ResetSandboxWorkspacePendingActionAttempt: + """Resetting a sandbox workspace is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of a climate preset activation. + :ivar action_type: Action attempt to track the status of resetting a sandbox workspace. :ivar error: Error associated with the action. @@ -1950,65 +2116,30 @@ class ActivateClimatePresetActionAttempt: :ivar status:""" - @dataclass - class Error(ResourceMapping): - """Error associated with the action. - - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar type: Type of the error.""" - - message: str - type: str - - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - type=d.get("type", None), - ) - - @dataclass - class Result(ResourceMapping): - """Result of the action.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - action_attempt_id: str - action_type: Literal["ACTIVATE_CLIMATE_PRESET"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["RESET_SANDBOX_WORKSPACE"] + error: None + result: None + status: Literal["pending"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class SimulateKeypadCodeEntryActionAttempt: - """Simulating a keypad code entry is pending. +class ResetSandboxWorkspaceErrorActionAttempt: + """Resetting a sandbox workspace is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of simulating a keypad code entry. + :ivar action_type: Action attempt to track the status of resetting a sandbox workspace. :ivar error: Error associated with the action. @@ -2034,47 +2165,30 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) - @dataclass - class Result(ResourceMapping): - """Result of the action.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - action_attempt_id: str - action_type: Literal["SIMULATE_KEYPAD_CODE_ENTRY"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["RESET_SANDBOX_WORKSPACE"] + error: Error + result: None + status: Literal["error"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class SimulateManualLockViaKeypadActionAttempt: - """Simulating a manual lock action using a keypad is pending. +class SetFanModeSuccessActionAttempt: + """Setting the fan mode is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of simulating a manual lock action using a keypad. + :ivar action_type: Action attempt to track the status of setting the fan mode on a thermostat. :ivar error: Error associated with the action. @@ -2082,24 +2196,6 @@ class SimulateManualLockViaKeypadActionAttempt: :ivar status:""" - @dataclass - class Error(ResourceMapping): - """Error associated with the action. - - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar type: Type of the error.""" - - message: str - type: str - - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - type=d.get("type", None), - ) - @dataclass class Result(ResourceMapping): """Result of the action.""" @@ -2110,37 +2206,29 @@ def from_dict(cls, d: Any): return cls() action_attempt_id: str - action_type: Literal["SIMULATE_MANUAL_LOCK_VIA_KEYPAD"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["SET_FAN_MODE"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class PushThermostatProgramsActionAttempt: - """Pushing thermostat weekly programs is pending. +class SetFanModePendingActionAttempt: + """Setting the fan mode is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of pushing thermostat programs. + :ivar action_type: Action attempt to track the status of setting the fan mode on a thermostat. :ivar error: Error associated with the action. @@ -2148,10 +2236,41 @@ class PushThermostatProgramsActionAttempt: :ivar status:""" - @dataclass - class Error(ResourceMapping): - """Error associated with the action. - + action_attempt_id: str + action_type: Literal["SET_FAN_MODE"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SetFanModeErrorActionAttempt: + """Setting the fan mode is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of setting the fan mode on a thermostat. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. :ivar type: Type of the error.""" @@ -2166,6 +2285,37 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) + action_attempt_id: str + action_type: Literal["SET_FAN_MODE"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SetHvacModeSuccessActionAttempt: + """Setting the HVAC mode is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of setting the HVAC mode on a thermostat. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + @dataclass class Result(ResourceMapping): """Result of the action.""" @@ -2176,37 +2326,180 @@ def from_dict(cls, d: Any): return cls() action_attempt_id: str - action_type: Literal["PUSH_THERMOSTAT_PROGRAMS"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["SET_HVAC_MODE"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class ConfigureAutoLockActionAttempt: - """Configuring the auto-lock is pending. +class SetHvacModePendingActionAttempt: + """Setting the HVAC mode is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Action attempt to track the status of configuring the auto-lock on a lock. + :ivar action_type: Action attempt to track the status of setting the HVAC mode on a thermostat. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["SET_HVAC_MODE"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SetHvacModeErrorActionAttempt: + """Setting the HVAC mode is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of setting the HVAC mode on a thermostat. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["SET_HVAC_MODE"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ActivateClimatePresetSuccessActionAttempt: + """Activating a climate preset is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of a climate preset activation. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action.""" + + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() + + action_attempt_id: str + action_type: Literal["ACTIVATE_CLIMATE_PRESET"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class ActivateClimatePresetPendingActionAttempt: + """Activating a climate preset is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of a climate preset activation. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["ACTIVATE_CLIMATE_PRESET"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ActivateClimatePresetErrorActionAttempt: + """Activating a climate preset is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of a climate preset activation. :ivar error: Error associated with the action. @@ -2232,47 +2525,951 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) + action_attempt_id: str + action_type: Literal["ACTIVATE_CLIMATE_PRESET"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SimulateKeypadCodeEntrySuccessActionAttempt: + """Simulating a keypad code entry is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of simulating a keypad code entry. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + @dataclass class Result(ResourceMapping): """Result of the action.""" - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() + + action_attempt_id: str + action_type: Literal["SIMULATE_KEYPAD_CODE_ENTRY"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class SimulateKeypadCodeEntryPendingActionAttempt: + """Simulating a keypad code entry is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of simulating a keypad code entry. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["SIMULATE_KEYPAD_CODE_ENTRY"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SimulateKeypadCodeEntryErrorActionAttempt: + """Simulating a keypad code entry is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of simulating a keypad code entry. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["SIMULATE_KEYPAD_CODE_ENTRY"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SimulateManualLockViaKeypadSuccessActionAttempt: + """Simulating a manual lock action using a keypad is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of simulating a manual lock action using a keypad. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action.""" + + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() + + action_attempt_id: str + action_type: Literal["SIMULATE_MANUAL_LOCK_VIA_KEYPAD"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class SimulateManualLockViaKeypadPendingActionAttempt: + """Simulating a manual lock action using a keypad is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of simulating a manual lock action using a keypad. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["SIMULATE_MANUAL_LOCK_VIA_KEYPAD"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SimulateManualLockViaKeypadErrorActionAttempt: + """Simulating a manual lock action using a keypad is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of simulating a manual lock action using a keypad. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["SIMULATE_MANUAL_LOCK_VIA_KEYPAD"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class PushThermostatProgramsSuccessActionAttempt: + """Pushing thermostat weekly programs is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of pushing thermostat programs. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action.""" + + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() + + action_attempt_id: str + action_type: Literal["PUSH_THERMOSTAT_PROGRAMS"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class PushThermostatProgramsPendingActionAttempt: + """Pushing thermostat weekly programs is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of pushing thermostat programs. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["PUSH_THERMOSTAT_PROGRAMS"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class PushThermostatProgramsErrorActionAttempt: + """Pushing thermostat weekly programs is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of pushing thermostat programs. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["PUSH_THERMOSTAT_PROGRAMS"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ConfigureAutoLockSuccessActionAttempt: + """Configuring the auto-lock is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of configuring the auto-lock on a lock. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action.""" + + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() + + action_attempt_id: str + action_type: Literal["CONFIGURE_AUTO_LOCK"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class ConfigureAutoLockPendingActionAttempt: + """Configuring the auto-lock is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of configuring the auto-lock on a lock. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["CONFIGURE_AUTO_LOCK"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class ConfigureAutoLockErrorActionAttempt: + """Configuring the auto-lock is pending. + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Action attempt to track the status of configuring the auto-lock on a lock. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["CONFIGURE_AUTO_LOCK"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SyncAccessCodesSuccessActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Syncing access codes is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action.""" + + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() + + action_attempt_id: str + action_type: Literal["SYNC_ACCESS_CODES"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class SyncAccessCodesPendingActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Syncing access codes is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["SYNC_ACCESS_CODES"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class SyncAccessCodesErrorActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Syncing access codes is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["SYNC_ACCESS_CODES"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class CreateAccessCodeSuccessActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Creating an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action. + + :ivar access_code: Created access code.""" + + access_code: Dict[str, Any] + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code=DeepAttrDict(d.get("access_code", None)), + ) + + action_attempt_id: str + action_type: Literal["CREATE_ACCESS_CODE"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class CreateAccessCodePendingActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Creating an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["CREATE_ACCESS_CODE"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class CreateAccessCodeErrorActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Creating an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["CREATE_ACCESS_CODE"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class DeleteAccessCodeSuccessActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Deleting an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action.""" + + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() + + action_attempt_id: str + action_type: Literal["DELETE_ACCESS_CODE"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class DeleteAccessCodePendingActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Deleting an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + action_attempt_id: str + action_type: Literal["DELETE_ACCESS_CODE"] + error: None + result: None + status: Literal["pending"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class DeleteAccessCodeErrorActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Deleting an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Error(ResourceMapping): + """Error associated with the action. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar type: Type of the error.""" + + message: str + type: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) + + action_attempt_id: str + action_type: Literal["DELETE_ACCESS_CODE"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class UpdateAccessCodeSuccessActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Updating an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + + @dataclass + class Result(ResourceMapping): + """Result of the action. + + :ivar access_code: Updated access code.""" + + access_code: Optional[Dict[str, Any]] + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code=DeepAttrDict(d.get("access_code", None)), + ) + + action_attempt_id: str + action_type: Literal["UPDATE_ACCESS_CODE"] + error: None + result: Result + status: Literal["success"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) + + +@dataclass +class UpdateAccessCodePendingActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Updating an access code is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" action_attempt_id: str - action_type: Literal["CONFIGURE_AUTO_LOCK"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["UPDATE_ACCESS_CODE"] + error: None + result: None + status: Literal["pending"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class SyncAccessCodesActionAttempt: +class UpdateAccessCodeErrorActionAttempt: """ :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Syncing access codes is pending. + :ivar action_type: Updating an access code is pending. :ivar error: Error associated with the action. @@ -2298,47 +3495,30 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) - @dataclass - class Result(ResourceMapping): - """Result of the action.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - action_attempt_id: str - action_type: Literal["SYNC_ACCESS_CODES"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["UPDATE_ACCESS_CODE"] + error: Error + result: None + status: Literal["error"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class CreateAccessCodeActionAttempt: +class CreateNoiseThresholdSuccessActionAttempt: """ :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Creating an access code is pending. + :ivar action_type: Creating a noise threshold is pending. :ivar error: Error associated with the action. @@ -2347,69 +3527,74 @@ class CreateAccessCodeActionAttempt: :ivar status:""" @dataclass - class Error(ResourceMapping): - """Error associated with the action. - - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + class Result(ResourceMapping): + """Result of the action. - :ivar type: Type of the error.""" + :ivar noise_threshold: Created noise threshold.""" - message: str - type: str + noise_threshold: Dict[str, Any] @classmethod def from_dict(cls, d: Any): return cls( - message=d.get("message", None), - type=d.get("type", None), + noise_threshold=DeepAttrDict(d.get("noise_threshold", None)), ) - @dataclass - class Result(ResourceMapping): - """Result of the action. + action_attempt_id: str + action_type: Literal["CREATE_NOISE_THRESHOLD"] + error: None + result: Result + status: Literal["success"] - :ivar access_code: Created access code.""" + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) - access_code: Dict[str, Any] - @classmethod - def from_dict(cls, d: Any): - return cls( - access_code=DeepAttrDict(d.get("access_code", None)), - ) +@dataclass +class CreateNoiseThresholdPendingActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Creating a noise threshold is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" action_attempt_id: str - action_type: Literal["CREATE_ACCESS_CODE"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["CREATE_NOISE_THRESHOLD"] + error: None + result: None + status: Literal["pending"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class DeleteAccessCodeActionAttempt: +class CreateNoiseThresholdErrorActionAttempt: """ :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Deleting an access code is pending. + :ivar action_type: Creating a noise threshold is pending. :ivar error: Error associated with the action. @@ -2435,47 +3620,30 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) - @dataclass - class Result(ResourceMapping): - """Result of the action.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - action_attempt_id: str - action_type: Literal["DELETE_ACCESS_CODE"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["CREATE_NOISE_THRESHOLD"] + error: Error + result: None + status: Literal["error"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class UpdateAccessCodeActionAttempt: +class DeleteNoiseThresholdSuccessActionAttempt: """ :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Updating an access code is pending. + :ivar action_type: Deleting a noise threshold is pending. :ivar error: Error associated with the action. @@ -2484,69 +3652,69 @@ class UpdateAccessCodeActionAttempt: :ivar status:""" @dataclass - class Error(ResourceMapping): - """Error associated with the action. + class Result(ResourceMapping): + """Result of the action.""" - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + @classmethod + def from_dict(cls, d: Any): + # pylint: disable=unused-argument + return cls() - :ivar type: Type of the error.""" + action_attempt_id: str + action_type: Literal["DELETE_NOISE_THRESHOLD"] + error: None + result: Result + status: Literal["success"] - message: str - type: str + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), + status=d.get("status", None), + ) - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - type=d.get("type", None), - ) - @dataclass - class Result(ResourceMapping): - """Result of the action. +@dataclass +class DeleteNoiseThresholdPendingActionAttempt: + """ - :ivar access_code: Updated access code.""" + :ivar action_attempt_id: ID of the action attempt. - access_code: Optional[Dict[str, Any]] + :ivar action_type: Deleting a noise threshold is pending. - @classmethod - def from_dict(cls, d: Any): - return cls( - access_code=DeepAttrDict(d.get("access_code", None)), - ) + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" action_attempt_id: str - action_type: Literal["UPDATE_ACCESS_CODE"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["DELETE_NOISE_THRESHOLD"] + error: None + result: None + status: Literal["pending"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class CreateNoiseThresholdActionAttempt: +class DeleteNoiseThresholdErrorActionAttempt: """ :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Creating a noise threshold is pending. + :ivar action_type: Deleting a noise threshold is pending. :ivar error: Error associated with the action. @@ -2572,11 +3740,42 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) + action_attempt_id: str + action_type: Literal["DELETE_NOISE_THRESHOLD"] + error: Error + result: None + status: Literal["error"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + action_attempt_id=d.get("action_attempt_id", None), + action_type=d.get("action_type", None), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), + status=d.get("status", None), + ) + + +@dataclass +class UpdateNoiseThresholdSuccessActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Updating a noise threshold is pending. + + :ivar error: Error associated with the action. + + :ivar result: Result of the action. + + :ivar status:""" + @dataclass class Result(ResourceMapping): """Result of the action. - :ivar noise_threshold: Created noise threshold.""" + :ivar noise_threshold: Updated noise threshold.""" noise_threshold: Dict[str, Any] @@ -2587,37 +3786,29 @@ def from_dict(cls, d: Any): ) action_attempt_id: str - action_type: Literal["CREATE_NOISE_THRESHOLD"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["UPDATE_NOISE_THRESHOLD"] + error: None + result: Result + status: Literal["success"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=cls.Result.from_dict(d.get("result") or {}), status=d.get("status", None), ) @dataclass -class DeleteNoiseThresholdActionAttempt: +class UpdateNoiseThresholdPendingActionAttempt: """ :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: Deleting a noise threshold is pending. + :ivar action_type: Updating a noise threshold is pending. :ivar error: Error associated with the action. @@ -2625,60 +3816,25 @@ class DeleteNoiseThresholdActionAttempt: :ivar status:""" - @dataclass - class Error(ResourceMapping): - """Error associated with the action. - - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar type: Type of the error.""" - - message: str - type: str - - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - type=d.get("type", None), - ) - - @dataclass - class Result(ResourceMapping): - """Result of the action.""" - - @classmethod - def from_dict(cls, d: Any): - # pylint: disable=unused-argument - return cls() - action_attempt_id: str - action_type: Literal["DELETE_NOISE_THRESHOLD"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + action_type: Literal["UPDATE_NOISE_THRESHOLD"] + error: None + result: None + status: Literal["pending"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=d.get("error", None), + result=d.get("result", None), status=d.get("status", None), ) @dataclass -class UpdateNoiseThresholdActionAttempt: +class UpdateNoiseThresholdErrorActionAttempt: """ :ivar action_attempt_id: ID of the action attempt. @@ -2709,101 +3865,382 @@ def from_dict(cls, d: Any): type=d.get("type", None), ) - @dataclass - class Result(ResourceMapping): - """Result of the action. - - :ivar noise_threshold: Updated noise threshold.""" - - noise_threshold: Dict[str, Any] - - @classmethod - def from_dict(cls, d: Any): - return cls( - noise_threshold=DeepAttrDict(d.get("noise_threshold", None)), - ) - action_attempt_id: str action_type: Literal["UPDATE_NOISE_THRESHOLD"] - error: Optional[Error] - result: Optional[Result] - status: Literal["success", "pending", "error"] + error: Error + result: None + status: Literal["error"] @classmethod def from_dict(cls, d: Any): return cls( action_attempt_id=d.get("action_attempt_id", None), action_type=d.get("action_type", None), - error=( - cls.Error.from_dict(d.get("error")) - if d.get("error") is not None - else None - ), - result=( - cls.Result.from_dict(d.get("result")) - if d.get("result") is not None - else None - ), + error=cls.Error.from_dict(d.get("error") or {}), + result=d.get("result", None), status=d.get("status", None), ) ActionAttempt = Union[ - LockDoorActionAttempt, - UnlockDoorActionAttempt, - ScanCredentialActionAttempt, - EncodeCredentialActionAttempt, - ScanToAssignCredentialActionAttempt, - AssignCredentialActionAttempt, - ResetSandboxWorkspaceActionAttempt, - SetFanModeActionAttempt, - SetHvacModeActionAttempt, - ActivateClimatePresetActionAttempt, - SimulateKeypadCodeEntryActionAttempt, - SimulateManualLockViaKeypadActionAttempt, - PushThermostatProgramsActionAttempt, - ConfigureAutoLockActionAttempt, - SyncAccessCodesActionAttempt, - CreateAccessCodeActionAttempt, - DeleteAccessCodeActionAttempt, - UpdateAccessCodeActionAttempt, - CreateNoiseThresholdActionAttempt, - DeleteNoiseThresholdActionAttempt, - UpdateNoiseThresholdActionAttempt, + LockDoorSuccessActionAttempt, + LockDoorPendingActionAttempt, + LockDoorErrorActionAttempt, + UnlockDoorSuccessActionAttempt, + UnlockDoorPendingActionAttempt, + UnlockDoorErrorActionAttempt, + ScanCredentialSuccessActionAttempt, + ScanCredentialPendingActionAttempt, + ScanCredentialErrorActionAttempt, + EncodeCredentialSuccessActionAttempt, + EncodeCredentialPendingActionAttempt, + EncodeCredentialErrorActionAttempt, + ScanToAssignCredentialSuccessActionAttempt, + ScanToAssignCredentialPendingActionAttempt, + ScanToAssignCredentialErrorActionAttempt, + AssignCredentialSuccessActionAttempt, + AssignCredentialPendingActionAttempt, + AssignCredentialErrorActionAttempt, + ResetSandboxWorkspaceSuccessActionAttempt, + ResetSandboxWorkspacePendingActionAttempt, + ResetSandboxWorkspaceErrorActionAttempt, + SetFanModeSuccessActionAttempt, + SetFanModePendingActionAttempt, + SetFanModeErrorActionAttempt, + SetHvacModeSuccessActionAttempt, + SetHvacModePendingActionAttempt, + SetHvacModeErrorActionAttempt, + ActivateClimatePresetSuccessActionAttempt, + ActivateClimatePresetPendingActionAttempt, + ActivateClimatePresetErrorActionAttempt, + SimulateKeypadCodeEntrySuccessActionAttempt, + SimulateKeypadCodeEntryPendingActionAttempt, + SimulateKeypadCodeEntryErrorActionAttempt, + SimulateManualLockViaKeypadSuccessActionAttempt, + SimulateManualLockViaKeypadPendingActionAttempt, + SimulateManualLockViaKeypadErrorActionAttempt, + PushThermostatProgramsSuccessActionAttempt, + PushThermostatProgramsPendingActionAttempt, + PushThermostatProgramsErrorActionAttempt, + ConfigureAutoLockSuccessActionAttempt, + ConfigureAutoLockPendingActionAttempt, + ConfigureAutoLockErrorActionAttempt, + SyncAccessCodesSuccessActionAttempt, + SyncAccessCodesPendingActionAttempt, + SyncAccessCodesErrorActionAttempt, + CreateAccessCodeSuccessActionAttempt, + CreateAccessCodePendingActionAttempt, + CreateAccessCodeErrorActionAttempt, + DeleteAccessCodeSuccessActionAttempt, + DeleteAccessCodePendingActionAttempt, + DeleteAccessCodeErrorActionAttempt, + UpdateAccessCodeSuccessActionAttempt, + UpdateAccessCodePendingActionAttempt, + UpdateAccessCodeErrorActionAttempt, + CreateNoiseThresholdSuccessActionAttempt, + CreateNoiseThresholdPendingActionAttempt, + CreateNoiseThresholdErrorActionAttempt, + DeleteNoiseThresholdSuccessActionAttempt, + DeleteNoiseThresholdPendingActionAttempt, + DeleteNoiseThresholdErrorActionAttempt, + UpdateNoiseThresholdSuccessActionAttempt, + UpdateNoiseThresholdPendingActionAttempt, + UpdateNoiseThresholdErrorActionAttempt, +] + +LockDoorActionAttempt = Union[ + LockDoorSuccessActionAttempt, + LockDoorPendingActionAttempt, + LockDoorErrorActionAttempt, +] + +UnlockDoorActionAttempt = Union[ + UnlockDoorSuccessActionAttempt, + UnlockDoorPendingActionAttempt, + UnlockDoorErrorActionAttempt, +] + +ScanCredentialActionAttempt = Union[ + ScanCredentialSuccessActionAttempt, + ScanCredentialPendingActionAttempt, + ScanCredentialErrorActionAttempt, +] + +EncodeCredentialActionAttempt = Union[ + EncodeCredentialSuccessActionAttempt, + EncodeCredentialPendingActionAttempt, + EncodeCredentialErrorActionAttempt, +] + +ScanToAssignCredentialActionAttempt = Union[ + ScanToAssignCredentialSuccessActionAttempt, + ScanToAssignCredentialPendingActionAttempt, + ScanToAssignCredentialErrorActionAttempt, +] + +AssignCredentialActionAttempt = Union[ + AssignCredentialSuccessActionAttempt, + AssignCredentialPendingActionAttempt, + AssignCredentialErrorActionAttempt, +] + +ResetSandboxWorkspaceActionAttempt = Union[ + ResetSandboxWorkspaceSuccessActionAttempt, + ResetSandboxWorkspacePendingActionAttempt, + ResetSandboxWorkspaceErrorActionAttempt, +] + +SetFanModeActionAttempt = Union[ + SetFanModeSuccessActionAttempt, + SetFanModePendingActionAttempt, + SetFanModeErrorActionAttempt, +] + +SetHvacModeActionAttempt = Union[ + SetHvacModeSuccessActionAttempt, + SetHvacModePendingActionAttempt, + SetHvacModeErrorActionAttempt, +] + +ActivateClimatePresetActionAttempt = Union[ + ActivateClimatePresetSuccessActionAttempt, + ActivateClimatePresetPendingActionAttempt, + ActivateClimatePresetErrorActionAttempt, +] + +SimulateKeypadCodeEntryActionAttempt = Union[ + SimulateKeypadCodeEntrySuccessActionAttempt, + SimulateKeypadCodeEntryPendingActionAttempt, + SimulateKeypadCodeEntryErrorActionAttempt, +] + +SimulateManualLockViaKeypadActionAttempt = Union[ + SimulateManualLockViaKeypadSuccessActionAttempt, + SimulateManualLockViaKeypadPendingActionAttempt, + SimulateManualLockViaKeypadErrorActionAttempt, +] + +PushThermostatProgramsActionAttempt = Union[ + PushThermostatProgramsSuccessActionAttempt, + PushThermostatProgramsPendingActionAttempt, + PushThermostatProgramsErrorActionAttempt, +] + +ConfigureAutoLockActionAttempt = Union[ + ConfigureAutoLockSuccessActionAttempt, + ConfigureAutoLockPendingActionAttempt, + ConfigureAutoLockErrorActionAttempt, +] + +SyncAccessCodesActionAttempt = Union[ + SyncAccessCodesSuccessActionAttempt, + SyncAccessCodesPendingActionAttempt, + SyncAccessCodesErrorActionAttempt, +] + +CreateAccessCodeActionAttempt = Union[ + CreateAccessCodeSuccessActionAttempt, + CreateAccessCodePendingActionAttempt, + CreateAccessCodeErrorActionAttempt, +] + +DeleteAccessCodeActionAttempt = Union[ + DeleteAccessCodeSuccessActionAttempt, + DeleteAccessCodePendingActionAttempt, + DeleteAccessCodeErrorActionAttempt, +] + +UpdateAccessCodeActionAttempt = Union[ + UpdateAccessCodeSuccessActionAttempt, + UpdateAccessCodePendingActionAttempt, + UpdateAccessCodeErrorActionAttempt, +] + +CreateNoiseThresholdActionAttempt = Union[ + CreateNoiseThresholdSuccessActionAttempt, + CreateNoiseThresholdPendingActionAttempt, + CreateNoiseThresholdErrorActionAttempt, +] + +DeleteNoiseThresholdActionAttempt = Union[ + DeleteNoiseThresholdSuccessActionAttempt, + DeleteNoiseThresholdPendingActionAttempt, + DeleteNoiseThresholdErrorActionAttempt, +] + +UpdateNoiseThresholdActionAttempt = Union[ + UpdateNoiseThresholdSuccessActionAttempt, + UpdateNoiseThresholdPendingActionAttempt, + UpdateNoiseThresholdErrorActionAttempt, +] + +SuccessActionAttempt = Union[ + LockDoorSuccessActionAttempt, + UnlockDoorSuccessActionAttempt, + ScanCredentialSuccessActionAttempt, + EncodeCredentialSuccessActionAttempt, + ScanToAssignCredentialSuccessActionAttempt, + AssignCredentialSuccessActionAttempt, + ResetSandboxWorkspaceSuccessActionAttempt, + SetFanModeSuccessActionAttempt, + SetHvacModeSuccessActionAttempt, + ActivateClimatePresetSuccessActionAttempt, + SimulateKeypadCodeEntrySuccessActionAttempt, + SimulateManualLockViaKeypadSuccessActionAttempt, + PushThermostatProgramsSuccessActionAttempt, + ConfigureAutoLockSuccessActionAttempt, + SyncAccessCodesSuccessActionAttempt, + CreateAccessCodeSuccessActionAttempt, + DeleteAccessCodeSuccessActionAttempt, + UpdateAccessCodeSuccessActionAttempt, + CreateNoiseThresholdSuccessActionAttempt, + DeleteNoiseThresholdSuccessActionAttempt, + UpdateNoiseThresholdSuccessActionAttempt, +] + +PendingActionAttempt = Union[ + LockDoorPendingActionAttempt, + UnlockDoorPendingActionAttempt, + ScanCredentialPendingActionAttempt, + EncodeCredentialPendingActionAttempt, + ScanToAssignCredentialPendingActionAttempt, + AssignCredentialPendingActionAttempt, + ResetSandboxWorkspacePendingActionAttempt, + SetFanModePendingActionAttempt, + SetHvacModePendingActionAttempt, + ActivateClimatePresetPendingActionAttempt, + SimulateKeypadCodeEntryPendingActionAttempt, + SimulateManualLockViaKeypadPendingActionAttempt, + PushThermostatProgramsPendingActionAttempt, + ConfigureAutoLockPendingActionAttempt, + SyncAccessCodesPendingActionAttempt, + CreateAccessCodePendingActionAttempt, + DeleteAccessCodePendingActionAttempt, + UpdateAccessCodePendingActionAttempt, + CreateNoiseThresholdPendingActionAttempt, + DeleteNoiseThresholdPendingActionAttempt, + UpdateNoiseThresholdPendingActionAttempt, +] + +ErrorActionAttempt = Union[ + LockDoorErrorActionAttempt, + UnlockDoorErrorActionAttempt, + ScanCredentialErrorActionAttempt, + EncodeCredentialErrorActionAttempt, + ScanToAssignCredentialErrorActionAttempt, + AssignCredentialErrorActionAttempt, + ResetSandboxWorkspaceErrorActionAttempt, + SetFanModeErrorActionAttempt, + SetHvacModeErrorActionAttempt, + ActivateClimatePresetErrorActionAttempt, + SimulateKeypadCodeEntryErrorActionAttempt, + SimulateManualLockViaKeypadErrorActionAttempt, + PushThermostatProgramsErrorActionAttempt, + ConfigureAutoLockErrorActionAttempt, + SyncAccessCodesErrorActionAttempt, + CreateAccessCodeErrorActionAttempt, + DeleteAccessCodeErrorActionAttempt, + UpdateAccessCodeErrorActionAttempt, + CreateNoiseThresholdErrorActionAttempt, + DeleteNoiseThresholdErrorActionAttempt, + UpdateNoiseThresholdErrorActionAttempt, ] -_ACTION_ATTEMPT_VARIANTS: Dict[str, Any] = { - "LOCK_DOOR": LockDoorActionAttempt, - "UNLOCK_DOOR": UnlockDoorActionAttempt, - "SCAN_CREDENTIAL": ScanCredentialActionAttempt, - "ENCODE_CREDENTIAL": EncodeCredentialActionAttempt, - "SCAN_TO_ASSIGN_CREDENTIAL": ScanToAssignCredentialActionAttempt, - "ASSIGN_CREDENTIAL": AssignCredentialActionAttempt, - "RESET_SANDBOX_WORKSPACE": ResetSandboxWorkspaceActionAttempt, - "SET_FAN_MODE": SetFanModeActionAttempt, - "SET_HVAC_MODE": SetHvacModeActionAttempt, - "ACTIVATE_CLIMATE_PRESET": ActivateClimatePresetActionAttempt, - "SIMULATE_KEYPAD_CODE_ENTRY": SimulateKeypadCodeEntryActionAttempt, - "SIMULATE_MANUAL_LOCK_VIA_KEYPAD": SimulateManualLockViaKeypadActionAttempt, - "PUSH_THERMOSTAT_PROGRAMS": PushThermostatProgramsActionAttempt, - "CONFIGURE_AUTO_LOCK": ConfigureAutoLockActionAttempt, - "SYNC_ACCESS_CODES": SyncAccessCodesActionAttempt, - "CREATE_ACCESS_CODE": CreateAccessCodeActionAttempt, - "DELETE_ACCESS_CODE": DeleteAccessCodeActionAttempt, - "UPDATE_ACCESS_CODE": UpdateAccessCodeActionAttempt, - "CREATE_NOISE_THRESHOLD": CreateNoiseThresholdActionAttempt, - "DELETE_NOISE_THRESHOLD": DeleteNoiseThresholdActionAttempt, - "UPDATE_NOISE_THRESHOLD": UpdateNoiseThresholdActionAttempt, +_ACTION_ATTEMPT_VARIANTS: Dict[Tuple[str, str], Any] = { + ("LOCK_DOOR", "success"): LockDoorSuccessActionAttempt, + ("LOCK_DOOR", "pending"): LockDoorPendingActionAttempt, + ("LOCK_DOOR", "error"): LockDoorErrorActionAttempt, + ("UNLOCK_DOOR", "success"): UnlockDoorSuccessActionAttempt, + ("UNLOCK_DOOR", "pending"): UnlockDoorPendingActionAttempt, + ("UNLOCK_DOOR", "error"): UnlockDoorErrorActionAttempt, + ("SCAN_CREDENTIAL", "success"): ScanCredentialSuccessActionAttempt, + ("SCAN_CREDENTIAL", "pending"): ScanCredentialPendingActionAttempt, + ("SCAN_CREDENTIAL", "error"): ScanCredentialErrorActionAttempt, + ("ENCODE_CREDENTIAL", "success"): EncodeCredentialSuccessActionAttempt, + ("ENCODE_CREDENTIAL", "pending"): EncodeCredentialPendingActionAttempt, + ("ENCODE_CREDENTIAL", "error"): EncodeCredentialErrorActionAttempt, + ( + "SCAN_TO_ASSIGN_CREDENTIAL", + "success", + ): ScanToAssignCredentialSuccessActionAttempt, + ( + "SCAN_TO_ASSIGN_CREDENTIAL", + "pending", + ): ScanToAssignCredentialPendingActionAttempt, + ("SCAN_TO_ASSIGN_CREDENTIAL", "error"): ScanToAssignCredentialErrorActionAttempt, + ("ASSIGN_CREDENTIAL", "success"): AssignCredentialSuccessActionAttempt, + ("ASSIGN_CREDENTIAL", "pending"): AssignCredentialPendingActionAttempt, + ("ASSIGN_CREDENTIAL", "error"): AssignCredentialErrorActionAttempt, + ("RESET_SANDBOX_WORKSPACE", "success"): ResetSandboxWorkspaceSuccessActionAttempt, + ("RESET_SANDBOX_WORKSPACE", "pending"): ResetSandboxWorkspacePendingActionAttempt, + ("RESET_SANDBOX_WORKSPACE", "error"): ResetSandboxWorkspaceErrorActionAttempt, + ("SET_FAN_MODE", "success"): SetFanModeSuccessActionAttempt, + ("SET_FAN_MODE", "pending"): SetFanModePendingActionAttempt, + ("SET_FAN_MODE", "error"): SetFanModeErrorActionAttempt, + ("SET_HVAC_MODE", "success"): SetHvacModeSuccessActionAttempt, + ("SET_HVAC_MODE", "pending"): SetHvacModePendingActionAttempt, + ("SET_HVAC_MODE", "error"): SetHvacModeErrorActionAttempt, + ("ACTIVATE_CLIMATE_PRESET", "success"): ActivateClimatePresetSuccessActionAttempt, + ("ACTIVATE_CLIMATE_PRESET", "pending"): ActivateClimatePresetPendingActionAttempt, + ("ACTIVATE_CLIMATE_PRESET", "error"): ActivateClimatePresetErrorActionAttempt, + ( + "SIMULATE_KEYPAD_CODE_ENTRY", + "success", + ): SimulateKeypadCodeEntrySuccessActionAttempt, + ( + "SIMULATE_KEYPAD_CODE_ENTRY", + "pending", + ): SimulateKeypadCodeEntryPendingActionAttempt, + ("SIMULATE_KEYPAD_CODE_ENTRY", "error"): SimulateKeypadCodeEntryErrorActionAttempt, + ( + "SIMULATE_MANUAL_LOCK_VIA_KEYPAD", + "success", + ): SimulateManualLockViaKeypadSuccessActionAttempt, + ( + "SIMULATE_MANUAL_LOCK_VIA_KEYPAD", + "pending", + ): SimulateManualLockViaKeypadPendingActionAttempt, + ( + "SIMULATE_MANUAL_LOCK_VIA_KEYPAD", + "error", + ): SimulateManualLockViaKeypadErrorActionAttempt, + ("PUSH_THERMOSTAT_PROGRAMS", "success"): PushThermostatProgramsSuccessActionAttempt, + ("PUSH_THERMOSTAT_PROGRAMS", "pending"): PushThermostatProgramsPendingActionAttempt, + ("PUSH_THERMOSTAT_PROGRAMS", "error"): PushThermostatProgramsErrorActionAttempt, + ("CONFIGURE_AUTO_LOCK", "success"): ConfigureAutoLockSuccessActionAttempt, + ("CONFIGURE_AUTO_LOCK", "pending"): ConfigureAutoLockPendingActionAttempt, + ("CONFIGURE_AUTO_LOCK", "error"): ConfigureAutoLockErrorActionAttempt, + ("SYNC_ACCESS_CODES", "success"): SyncAccessCodesSuccessActionAttempt, + ("SYNC_ACCESS_CODES", "pending"): SyncAccessCodesPendingActionAttempt, + ("SYNC_ACCESS_CODES", "error"): SyncAccessCodesErrorActionAttempt, + ("CREATE_ACCESS_CODE", "success"): CreateAccessCodeSuccessActionAttempt, + ("CREATE_ACCESS_CODE", "pending"): CreateAccessCodePendingActionAttempt, + ("CREATE_ACCESS_CODE", "error"): CreateAccessCodeErrorActionAttempt, + ("DELETE_ACCESS_CODE", "success"): DeleteAccessCodeSuccessActionAttempt, + ("DELETE_ACCESS_CODE", "pending"): DeleteAccessCodePendingActionAttempt, + ("DELETE_ACCESS_CODE", "error"): DeleteAccessCodeErrorActionAttempt, + ("UPDATE_ACCESS_CODE", "success"): UpdateAccessCodeSuccessActionAttempt, + ("UPDATE_ACCESS_CODE", "pending"): UpdateAccessCodePendingActionAttempt, + ("UPDATE_ACCESS_CODE", "error"): UpdateAccessCodeErrorActionAttempt, + ("CREATE_NOISE_THRESHOLD", "success"): CreateNoiseThresholdSuccessActionAttempt, + ("CREATE_NOISE_THRESHOLD", "pending"): CreateNoiseThresholdPendingActionAttempt, + ("CREATE_NOISE_THRESHOLD", "error"): CreateNoiseThresholdErrorActionAttempt, + ("DELETE_NOISE_THRESHOLD", "success"): DeleteNoiseThresholdSuccessActionAttempt, + ("DELETE_NOISE_THRESHOLD", "pending"): DeleteNoiseThresholdPendingActionAttempt, + ("DELETE_NOISE_THRESHOLD", "error"): DeleteNoiseThresholdErrorActionAttempt, + ("UPDATE_NOISE_THRESHOLD", "success"): UpdateNoiseThresholdSuccessActionAttempt, + ("UPDATE_NOISE_THRESHOLD", "pending"): UpdateNoiseThresholdPendingActionAttempt, + ("UPDATE_NOISE_THRESHOLD", "error"): UpdateNoiseThresholdErrorActionAttempt, } def action_attempt_from_dict(d: Any) -> ActionAttempt: - """Deserialize a known action_type variant. + """Deserialize a known action_type and status variant. Unknown discriminator values return ``DeepAttrDict`` so payloads from a newer API remain readable. The static return type covers known variants. """ - variant = _ACTION_ATTEMPT_VARIANTS.get(d.get("action_type")) + variant = _ACTION_ATTEMPT_VARIANTS.get((d.get("action_type"), d.get("status"))) if variant is None: return cast(ActionAttempt, DeepAttrDict(d)) return variant.from_dict(d) diff --git a/test/action_attempt_types_test.py b/test/action_attempt_types_test.py new file mode 100644 index 00000000..34425576 --- /dev/null +++ b/test/action_attempt_types_test.py @@ -0,0 +1,153 @@ +# mypy: warn_unused_ignores=True + +from typing import Any, Literal, assert_type, cast + +from seam.client import SeamHttpClient +from seam.modules.action_attempts import poll_until_ready +from seam.resources import ( + ErrorActionAttempt, + LockDoorActionAttempt, + LockDoorErrorActionAttempt, + LockDoorPendingActionAttempt, + LockDoorSuccessActionAttempt, + PendingActionAttempt, + SuccessActionAttempt, + action_attempt_from_dict, +) + + +def _assert_unnarrowed_dereference_is_rejected( + attempt: LockDoorActionAttempt, +) -> None: + _ = attempt.result.was_confirmed_by_device # type: ignore[union-attr] + _ = attempt.error.message # type: ignore[union-attr] + + +def _assert_success_narrowing_needs_no_none_check( + attempt: LockDoorActionAttempt, +) -> None: + if attempt.status == "success": + assert_type(attempt, LockDoorSuccessActionAttempt) + assert_type(attempt.result, LockDoorSuccessActionAttempt.Result) + assert_type(attempt.result.was_confirmed_by_device, bool | None) + assert_type(attempt.error, None) + + +def _assert_error_narrowing_needs_no_none_check( + attempt: LockDoorActionAttempt, +) -> None: + if attempt.status == "error": + assert_type(attempt, LockDoorErrorActionAttempt) + assert_type(attempt.error, LockDoorErrorActionAttempt.Error) + assert_type(attempt.error.message, str) + assert_type(attempt.error.type, str) + assert_type(attempt.result, None) + + +def _assert_isinstance_narrowing_needs_no_none_check( + attempt: LockDoorActionAttempt, +) -> None: + if isinstance(attempt, LockDoorSuccessActionAttempt): + assert_type(attempt.result, LockDoorSuccessActionAttempt.Result) + if isinstance(attempt, LockDoorErrorActionAttempt): + assert_type(attempt.error, LockDoorErrorActionAttempt.Error) + + +def _assert_waiting_returns_the_success_union(client: SeamHttpClient) -> None: + attempt = poll_until_ready(client, action_attempt_id="attempt-id") + assert_type(attempt, SuccessActionAttempt) + assert_type(attempt.status, Literal["success"]) + if attempt.action_type == "LOCK_DOOR": + assert_type(attempt, LockDoorSuccessActionAttempt) + assert_type(attempt.result, LockDoorSuccessActionAttempt.Result) + assert_type(attempt.error, None) + + +def _assert_pending_members_type_dependents_as_none( + attempt: LockDoorPendingActionAttempt, +) -> None: + assert_type(attempt.status, Literal["pending"]) + assert_type(attempt.error, None) + assert_type(attempt.result, None) + + +def _assert_status_unions_cover_every_action_type( + pending: PendingActionAttempt, + failed: ErrorActionAttempt, +) -> None: + assert_type(pending.status, Literal["pending"]) + assert_type(pending.error, None) + assert_type(pending.result, None) + assert_type(failed.status, Literal["error"]) + assert_type(failed.result, None) + _ = failed.error.message + + +def test_from_dict_parses_a_pending_action_attempt(): + attempt = action_attempt_from_dict( + { + "action_attempt_id": "attempt-id", + "action_type": "LOCK_DOOR", + "status": "pending", + } + ) + + assert isinstance(attempt, LockDoorPendingActionAttempt) + assert attempt.status == "pending" + assert attempt.result is None + assert attempt.error is None + + +def test_from_dict_parses_a_successful_action_attempt(): + attempt = action_attempt_from_dict( + { + "action_attempt_id": "attempt-id", + "action_type": "LOCK_DOOR", + "status": "success", + "result": {"was_confirmed_by_device": True}, + } + ) + + assert isinstance(attempt, LockDoorSuccessActionAttempt) + assert attempt.status == "success" + assert isinstance(attempt.result, LockDoorSuccessActionAttempt.Result) + assert attempt.result.was_confirmed_by_device is True + assert attempt.error is None + + +def test_from_dict_parses_a_failed_action_attempt(): + attempt = action_attempt_from_dict( + { + "action_attempt_id": "attempt-id", + "action_type": "LOCK_DOOR", + "status": "error", + "error": {"message": "failed", "type": "device_error"}, + } + ) + + assert isinstance(attempt, LockDoorErrorActionAttempt) + assert attempt.status == "error" + assert isinstance(attempt.error, LockDoorErrorActionAttempt.Error) + assert attempt.error.message == "failed" + assert attempt.error.type == "device_error" + assert attempt.result is None + + +def test_from_dict_keeps_unknown_statuses_readable(): + unknown = cast( + Any, + action_attempt_from_dict({"action_type": "LOCK_DOOR", "status": "cancelled"}), + ) + + assert unknown.action_type == "LOCK_DOOR" + assert unknown.status == "cancelled" + + +def test_action_attempt_types_narrow_on_status(): + assert callable(_assert_unnarrowed_dereference_is_rejected) + assert callable(_assert_success_narrowing_needs_no_none_check) + assert callable(_assert_error_narrowing_needs_no_none_check) + assert callable(_assert_isinstance_narrowing_needs_no_none_check) + assert callable(_assert_waiting_returns_the_success_union) + assert callable(_assert_pending_members_type_dependents_as_none) + assert callable(_assert_status_unions_cover_every_action_type) diff --git a/test/nested_resource_test.py b/test/nested_resource_test.py index f0654b20..6772ff85 100644 --- a/test/nested_resource_test.py +++ b/test/nested_resource_test.py @@ -8,8 +8,10 @@ import seam.resources.device as device_module from seam.resources.acs_user import AcsUser from seam.resources.action_attempt import ( - LockDoorActionAttempt, - ScanCredentialActionAttempt, + LockDoorErrorActionAttempt, + LockDoorPendingActionAttempt, + LockDoorSuccessActionAttempt, + ScanCredentialSuccessActionAttempt, action_attempt_from_dict, ) from seam.resources.device import Device @@ -75,28 +77,42 @@ def test_action_attempt_union_hydrates_nested_result_and_error(): attempt = action_attempt_from_dict( { "action_type": "LOCK_DOOR", + "status": "success", "result": {"was_confirmed_by_device": True}, - "error": {"message": "failed", "type": "device_error"}, } ) - assert isinstance(attempt, LockDoorActionAttempt) - assert isinstance(attempt.result, LockDoorActionAttempt.Result) + assert isinstance(attempt, LockDoorSuccessActionAttempt) + assert isinstance(attempt.result, LockDoorSuccessActionAttempt.Result) assert attempt.result.was_confirmed_by_device is True - assert isinstance(attempt.error, LockDoorActionAttempt.Error) - assert attempt.error.message == "failed" + assert attempt.error is None + + failed = action_attempt_from_dict( + { + "action_type": "LOCK_DOOR", + "status": "error", + "error": {"message": "failed", "type": "device_error"}, + } + ) + assert isinstance(failed, LockDoorErrorActionAttempt) + assert isinstance(failed.error, LockDoorErrorActionAttempt.Error) + assert failed.error.message == "failed" + assert failed.result is None pending = action_attempt_from_dict( {"action_type": "LOCK_DOOR", "status": "pending"} ) + assert isinstance(pending, LockDoorPendingActionAttempt) assert pending.error is None assert pending.result is None def test_action_attempt_variants_keep_distinct_result_shapes(): - lock_fields = {f.name for f in dataclasses.fields(LockDoorActionAttempt.Result)} + lock_fields = { + f.name for f in dataclasses.fields(LockDoorSuccessActionAttempt.Result) + } scan_fields = { - f.name for f in dataclasses.fields(ScanCredentialActionAttempt.Result) + f.name for f in dataclasses.fields(ScanCredentialSuccessActionAttempt.Result) } assert "was_confirmed_by_device" in lock_fields diff --git a/test/resource_types_test.py b/test/resource_types_test.py index e2b29c9e..3c9cc9ca 100644 --- a/test/resource_types_test.py +++ b/test/resource_types_test.py @@ -8,8 +8,9 @@ ActionAttempt, Device, LockDoorActionAttempt, + LockDoorSuccessActionAttempt, NoiseSensorNoiseThresholdTriggeredEvent, - ScanCredentialActionAttempt, + ScanCredentialSuccessActionAttempt, SeamEvent, UnmanagedAccessCode, ) @@ -27,7 +28,7 @@ def _assert_access_code_narrowing( def _assert_boolean_shapes( code: AccessCode, unmanaged_code: UnmanagedAccessCode, - credential: ScanCredentialActionAttempt.Result.AcsCredentialOnSeam, + credential: ScanCredentialSuccessActionAttempt.Result.AcsCredentialOnSeam, ) -> None: assert_type(code.is_backup_access_code_available, bool) error = code.errors[0] @@ -63,8 +64,11 @@ def _assert_action_attempt_narrowing(attempt: ActionAttempt) -> None: assert_type(attempt, LockDoorActionAttempt) assert_type( attempt.result, - LockDoorActionAttempt.Result | None, + LockDoorSuccessActionAttempt.Result | None, ) + if attempt.status == "success": + assert_type(attempt, LockDoorSuccessActionAttempt) + assert_type(attempt.result, LockDoorSuccessActionAttempt.Result) def _assert_record_value_types(device: Device) -> None: