From ffd60985a1a8a2e20ae50fcef0b0e9b17ffbfbd5 Mon Sep 17 00:00:00 2001 From: Evan Sosenko Date: Wed, 19 Aug 2026 12:44:02 -0700 Subject: [PATCH] feat: restore discriminated union types --- .../layouts/partials/resource-dataclass.hbs | 13 +- codegen/layouts/partials/route-method.hbs | 6 +- codegen/layouts/resource.hbs | 37 +- codegen/layouts/resources-index.hbs | 2 +- codegen/lib/handlebars-helpers.ts | 8 + codegen/lib/layouts/resources.ts | 288 +- codegen/lib/layouts/route.ts | 7 + codegen/lib/python-type.ts | 42 +- seam/modules/action_attempts.py | 6 +- seam/resources/__init__.py | 139 +- seam/resources/access_code.py | 1429 ++- seam/resources/access_grant.py | 343 +- seam/resources/access_method.py | 281 +- seam/resources/acs_access_group.py | 399 +- seam/resources/acs_credential.py | 204 +- seam/resources/acs_encoder.py | 2 +- seam/resources/acs_entrance.py | 137 +- seam/resources/acs_system.py | 376 +- seam/resources/acs_user.py | 715 +- seam/resources/action_attempt.py | 2393 ++++- seam/resources/connect_webview.py | 8 +- seam/resources/connected_account.py | 323 +- seam/resources/device.py | 1425 ++- seam/resources/device_provider.py | 80 +- seam/resources/phone.py | 2 +- seam/resources/seam_event.py | 8439 ++++++++++++++++- seam/resources/unmanaged_access_code.py | 973 +- seam/resources/unmanaged_access_grant.py | 343 +- seam/resources/unmanaged_access_method.py | 281 +- seam/resources/unmanaged_device.py | 1464 ++- seam/resources/unmanaged_user_identity.py | 63 +- seam/resources/user_identity.py | 63 +- seam/resources/workspace.py | 2 +- seam/routes/access_codes.py | 32 +- seam/routes/access_grants.py | 120 +- seam/routes/access_methods.py | 134 +- seam/routes/acs_credentials.py | 8 +- seam/routes/acs_encoders.py | 14 +- seam/routes/acs_encoders_simulate.py | 124 +- seam/routes/acs_entrances.py | 31 +- seam/routes/action_attempts.py | 10 +- seam/routes/connect_webviews.py | 388 +- seam/routes/connected_accounts.py | 32 +- seam/routes/customers.py | 68 +- seam/routes/devices.py | 680 +- seam/routes/devices_unmanaged.py | 620 +- seam/routes/events.py | 930 +- seam/routes/locks.py | 462 +- seam/routes/locks_simulate.py | 10 +- seam/routes/noise_sensors.py | 40 +- seam/routes/spaces.py | 104 +- seam/routes/thermostats.py | 258 +- seam/routes/thermostats_daily_programs.py | 6 +- seam/routes/thermostats_simulate.py | 8 +- seam/routes/workspaces.py | 14 +- seam/seam_webhook.py | 4 +- test/nested_resource_test.py | 106 +- test/resource_types_test.py | 53 +- 58 files changed, 22544 insertions(+), 2005 deletions(-) diff --git a/codegen/layouts/partials/resource-dataclass.hbs b/codegen/layouts/partials/resource-dataclass.hbs index 7375389b..e52750c6 100644 --- a/codegen/layouts/partials/resource-dataclass.hbs +++ b/codegen/layouts/partials/resource-dataclass.hbs @@ -10,6 +10,17 @@ {{#each nestedClasses}} {{> resource-dataclass isNested=true}} +{{/each}} +{{#each nestedUnions}} +{{../memberIndent}}{{className}} = Union[{{#each variants}}{{className}}{{#unless @last}}, {{/unless}}{{/each}}] +{{../memberIndent}}_{{className}}Variants = { +{{#each variants}} +{{#each values}} +{{../../../memberIndent}} {{pythonString this}}: {{../className}}, +{{/each}} +{{/each}} +{{../memberIndent}} } + {{/each}} {{#each properties}} {{../memberIndent}}{{pythonIdentifier name}}: {{type}} @@ -22,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 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}}, +{{../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}}, {{/each}} {{memberIndent}} ) diff --git a/codegen/layouts/partials/route-method.hbs b/codegen/layouts/partials/route-method.hbs index d86bd6d5..ad5454d6 100644 --- a/codegen/layouts/partials/route-method.hbs +++ b/codegen/layouts/partials/route-method.hbs @@ -24,7 +24,7 @@ return {{#if isAsync}}await resolve_action_attempt_async{{else}}resolve_action_attempt{{/if}}( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt ) {{else if (eq returnType "None")}} @@ -32,8 +32,8 @@ return None {{else if (isListType returnType)}} - return [{{listItemType returnType}}.from_dict(item) for item in res{{#each returnPath}}["{{this}}"]{{/each}}] + return [{{fromDict (listItemType returnType)}}(item) for item in res{{#each returnPath}}["{{this}}"]{{/each}}] {{else}} - return {{returnType}}.from_dict(res{{#each returnPath}}["{{this}}"]{{/each}}) + return {{fromDict returnType}}(res{{#each returnPath}}["{{this}}"]{{/each}}) {{/if}} diff --git a/codegen/layouts/resource.hbs b/codegen/layouts/resource.hbs index 40da5595..72c9250b 100644 --- a/codegen/layouts/resource.hbs +++ b/codegen/layouts/resource.hbs @@ -1,7 +1,42 @@ -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union{{#if union}}, cast{{/if}} from dataclasses import dataclass from ..deep_attr_dict import DeepAttrDict from ..resource_mapping import ResourceMapping +{{#if hasDiscriminatedLists}} +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) +{{/if}} + +{{#each classes}} {{> resource-dataclass}} + + +{{/each}} +{{#if union}} +{{union.className}} = Union[{{#each union.variants}}{{className}}{{#unless @last}}, {{/unless}}{{/each}}] + +{{union.variantsName}}: Dict[str, Any] = { +{{#each union.variants}} +{{#each values}} + {{pythonString this}}: {{../className}}, +{{/each}} +{{/each}} +} + + +def {{union.fromDictName}}(d: Any) -> {{union.className}}: + """Deserialize a known {{union.discriminator}} 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}}")) + if variant is None: + return cast({{union.className}}, DeepAttrDict(d)) + return variant.from_dict(d) +{{/if}} diff --git a/codegen/layouts/resources-index.hbs b/codegen/layouts/resources-index.hbs index 4e79ef1a..2f9bf062 100644 --- a/codegen/layouts/resources-index.hbs +++ b/codegen/layouts/resources-index.hbs @@ -1,3 +1,3 @@ {{#each resources}} -from .{{moduleName}} import {{className}} +from .{{moduleName}} import ({{#each exports}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}) {{/each}} diff --git a/codegen/lib/handlebars-helpers.ts b/codegen/lib/handlebars-helpers.ts index 4b8a4227..deca6966 100644 --- a/codegen/lib/handlebars-helpers.ts +++ b/codegen/lib/handlebars-helpers.ts @@ -53,6 +53,8 @@ export const indent = (value: string, spaces: number): string => export const pythonIdentifier = (name: string): string => PYTHON_KEYWORDS.has(name) ? `${name}_` : name +export const pythonString = (value: string): string => JSON.stringify(value) + // A param the API documents as nullable may be set to the NULL sentinel, which // the client serializes to null. Params that are merely optional may not: they // are omitted by passing None, and sending null would unset a value instead. @@ -62,3 +64,9 @@ export const nullableType = (type: string, isNullable: boolean): string => export const isListType = (type: string): boolean => type.startsWith('List[') export const listItemType = (type: string): string => type.slice(5, -1) + +export const fromDict = (type: string): string => { + if (type === 'SeamEvent') return 'seam_event_from_dict' + if (type === 'ActionAttempt') return 'action_attempt_from_dict' + return `${type}.from_dict` +} diff --git a/codegen/lib/layouts/resources.ts b/codegen/lib/layouts/resources.ts index 9e51d72e..53ffbcb1 100644 --- a/codegen/lib/layouts/resources.ts +++ b/codegen/lib/layouts/resources.ts @@ -11,22 +11,38 @@ import { mapRequiredPropertyToPythonType, } from '../python-type.js' -export interface ResourceLayoutContext extends ResourceClassLayoutContext { +export interface ResourceLayoutContext { + className: string moduleName: string isDeprecated: boolean deprecationMessage: string + classes: ResourceClassLayoutContext[] + union?: DiscriminatedUnionLayoutContext + hasDiscriminatedLists: boolean + exports: string[] } interface ResourceClassLayoutContext { className: string description: string + isDeprecated?: boolean + deprecationMessage?: string classIndent: string memberIndent: string docIndent: number nestedClasses: ResourceClassLayoutContext[] + nestedUnions: DiscriminatedUnionLayoutContext[] properties: ResourcePropertyLayoutContext[] } +interface DiscriminatedUnionLayoutContext { + className: string + discriminator: string + fromDictName: string + variantsName: string + variants: Array<{ className: string; values: string[] }> +} + interface ResourcePropertyLayoutContext { name: string description: string @@ -37,15 +53,17 @@ interface ResourcePropertyLayoutContext { isDictParam: boolean isObject: boolean isObjectList: boolean + isDiscriminatedObjectList: boolean + discriminator: string } export interface ResourcesIndexLayoutContext { - resources: Array<{ className: string; moduleName: string }> + resources: Array<{ exports: string[]; moduleName: string }> } -// The action attempt and event variants each generate a single dataclass with -// the union of the variant properties. -const mergeResourceProperties = ( +// Kept public because Ruby and PHP share these exact merge semantics. Union +// generation bypasses merging rather than changing it. +export const mergeResourceProperties = ( resources: Array<{ properties: Property[] }>, ): Property[] => mergePropertyLists(resources.map(({ properties }) => properties)) @@ -223,6 +241,7 @@ const reservedClassNames = new Set([ 'DeepAttrDict', 'Dict', 'List', + 'Literal', 'Optional', 'ResourceMapping', 'Union', @@ -234,15 +253,31 @@ const getNestedProperties = (property: Property): Property[] | undefined => { if (property.format === 'list' && property.itemFormat === 'object') { return property.itemProperties } - if ( - property.format === 'list' && - property.itemFormat === 'discriminated_object' - ) { - return mergeResourceProperties(property.variants) - } return undefined } +const getDiscriminatorValues = ( + properties: Property[], + discriminator: string, + path: string, +): string[] => { + const property = properties.find(({ name }) => name === discriminator) + if (property?.format !== 'enum' || property.values.length === 0) { + throw new Error( + `${path} must have an enum property named ${discriminator} to generate a discriminated union.`, + ) + } + return property.values.map(({ name }) => name) +} + +const singular = (name: string): string => + name.endsWith('s') ? name.slice(0, -1) : name + +const pythonClassName = (value: string): string => { + const name = pascalCase(value).replaceAll('_', '') + return /^[A-Z]/.test(name) ? name : `Variant${name}` +} + const buildClass = ( className: string, description: string, @@ -257,14 +292,64 @@ const buildClass = ( } const nestedClasses: ResourceClassLayoutContext[] = [] + const nestedUnions: DiscriminatedUnionLayoutContext[] = [] const takenClassNames = new Set() const properties = classProperties.map((property) => { const nestedProperties = getNestedProperties(property) const nestedPath = `${path}.${property.name}` + const isDiscriminatedObjectList = + property.format === 'list' && + property.itemFormat === 'discriminated_object' let nestedClassName: string | undefined - if (nestedProperties != null) { + if (isDiscriminatedObjectList) { + nestedClassName = pascalCase(property.name) + if ( + reservedClassNames.has(nestedClassName) || + takenClassNames.has(nestedClassName) + ) { + throw new Error( + `${nestedPath} would generate a duplicate or reserved union named ${nestedClassName}.`, + ) + } + takenClassNames.add(nestedClassName) + const suffix = pascalCase(singular(property.name)) + const variants = property.variants.map((variant) => { + const values = getDiscriminatorValues( + variant.properties, + property.discriminator, + nestedPath, + ) + const variantClassName = `${pythonClassName(values[0] ?? '')}${suffix}` + if ( + reservedClassNames.has(variantClassName) || + takenClassNames.has(variantClassName) + ) { + throw new Error( + `${nestedPath} would generate a duplicate or reserved nested class named ${variantClassName}.`, + ) + } + takenClassNames.add(variantClassName) + nestedClasses.push( + buildClass( + variantClassName, + variant.description, + variant.properties, + `${nestedPath}.${values[0]}`, + indentation + 4, + ), + ) + return { className: variantClassName, values } + }) + nestedUnions.push({ + className: nestedClassName, + discriminator: property.discriminator, + fromDictName: '', + variantsName: '', + variants, + }) + } else if (nestedProperties != null) { // Each class scopes its own nested classes, so the property name alone // names them unambiguously. nestedClassName = pascalCase(property.name) @@ -315,7 +400,12 @@ const buildClass = ( nestedClassName: nestedClassName ?? '', isDictParam: requiredType.startsWith('Dict'), isObject, - isObjectList: nestedClassName != null && property.format === 'list', + isObjectList: + !isDiscriminatedObjectList && + nestedClassName != null && + property.format === 'list', + isDiscriminatedObjectList, + discriminator: isDiscriminatedObjectList ? property.discriminator : '', } }) @@ -326,10 +416,70 @@ const buildClass = ( memberIndent: ' '.repeat(indentation + 4), docIndent: indentation + 4, nestedClasses, + nestedUnions, properties, } } +const hasDiscriminatedLists = ( + resourceClass: ResourceClassLayoutContext, +): boolean => + resourceClass.nestedUnions.length > 0 || + resourceClass.nestedClasses.some(hasDiscriminatedLists) + +const buildUnionResource = ( + className: string, + discriminator: string, + fromDictName: string, + variants: Array<{ + value: string + description: string + properties: Property[] + isDeprecated: boolean + deprecationMessage: string + }>, + isDeprecated: boolean, + deprecationMessage: 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 union = { + className, + discriminator, + fromDictName, + variantsName: `_${snakeCase(className).toUpperCase()}_VARIANTS`, + variants: classes.map((variantClass, index) => ({ + className: variantClass.className, + values: [variants[index]?.value ?? ''], + })), + } + + return { + className, + moduleName: snakeCase(className), + isDeprecated, + deprecationMessage, + classes, + union, + hasDiscriminatedLists: classes.some(hasDiscriminatedLists), + exports: [ + ...classes.map(({ className: name }) => name), + className, + fromDictName, + ], + } +} + export const getResourceLayoutContexts = ( blueprint: Blueprint, ): ResourceLayoutContext[] => { @@ -341,31 +491,17 @@ export const getResourceLayoutContexts = ( isDeprecated: boolean deprecationMessage: string } - >() + >(blueprint.resources.map((resource) => [resource.resourceType, resource])) - for (const resource of blueprint.resources) { - models.set(resource.resourceType, resource) + const discriminatedResourceTypes = new Set( + [...blueprint.events, ...blueprint.actionAttempts].map( + ({ resourceType }) => resourceType, + ), + ) + for (const resourceType of discriminatedResourceTypes) { + models.delete(resourceType) } - // The event and action attempt variants merge into a single dataclass with - // the union of the variant properties, overriding the base resource schema. - const actionAttemptModel = models.get('action_attempt') - models.set('action_attempt', { - properties: mergeResourceProperties(blueprint.actionAttempts), - description: - actionAttemptModel?.description ?? - 'An attempt to perform an action in the Seam API.', - isDeprecated: actionAttemptModel?.isDeprecated ?? false, - deprecationMessage: actionAttemptModel?.deprecationMessage ?? '', - }) - const eventModel = models.get('event') - models.set('event', { - properties: mergeResourceProperties(blueprint.events), - description: eventModel?.description ?? 'An event emitted by the Seam API.', - isDeprecated: eventModel?.isDeprecated ?? false, - deprecationMessage: eventModel?.deprecationMessage ?? '', - }) - if (blueprint.pagination != null) { models.set('pagination', { properties: blueprint.pagination.properties, @@ -375,37 +511,83 @@ export const getResourceLayoutContexts = ( }) } - return [...models.entries()] - .map(([name, model]) => { + const resources: ResourceLayoutContext[] = [...models.entries()].map( + ([name, model]) => { const { properties, description, isDeprecated, deprecationMessage } = model const className = pascalCase(convertCustomResourceName(name)) - const rootClass = buildClass( - className, - description, - properties, - name, - rootIndentation, - ) + const rootClass = { + ...buildClass( + className, + description, + properties, + name, + rootIndentation, + ), + isDeprecated, + deprecationMessage, + } return { - ...rootClass, + className, + moduleName: snakeCase(className), isDeprecated, deprecationMessage, - // Derived from the class name rather than the resource type so the - // module always matches the dataclass it exports (e.g. the "event" - // resource becomes SeamEvent in seam_event.py). - moduleName: snakeCase(className), + classes: [rootClass], + hasDiscriminatedLists: hasDiscriminatedLists(rootClass), + exports: [className], } - }) - .sort((a, b) => (a.moduleName < b.moduleName ? -1 : 1)) + }, + ) + + const eventModel = blueprint.resources.find( + ({ resourceType }) => resourceType === 'event', + ) + resources.push( + buildUnionResource( + 'SeamEvent', + 'event_type', + 'seam_event_from_dict', + blueprint.events.map((event) => ({ + value: event.eventType, + description: event.description, + properties: event.properties, + isDeprecated: event.isDeprecated, + deprecationMessage: event.deprecationMessage, + })), + eventModel?.isDeprecated ?? false, + eventModel?.deprecationMessage ?? '', + ), + ) + + const actionAttemptModel = blueprint.resources.find( + ({ resourceType }) => resourceType === 'action_attempt', + ) + resources.push( + buildUnionResource( + '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, + })), + actionAttemptModel?.isDeprecated ?? false, + actionAttemptModel?.deprecationMessage ?? '', + ), + ) + + return resources.sort((a, b) => (a.moduleName < b.moduleName ? -1 : 1)) } export const setResourcesIndexLayoutContext = ( resources: ResourceLayoutContext[], ): ResourcesIndexLayoutContext => ({ - resources: resources.map(({ className, moduleName }) => ({ - className, + resources: resources.map(({ exports, moduleName }) => ({ + exports, moduleName, })), }) diff --git a/codegen/lib/layouts/route.ts b/codegen/lib/layouts/route.ts index 1ff722e1..029f2ea0 100644 --- a/codegen/lib/layouts/route.ts +++ b/codegen/lib/layouts/route.ts @@ -114,6 +114,13 @@ export const setRouteLayoutContext = (cls: ClassModel): RouteLayoutContext => { ({ returnResource }) => returnResource === 'ActionAttempt', ) + if (resourceClasses.includes('ActionAttempt')) { + resourceClasses.push('action_attempt_from_dict') + } + if (resourceClasses.includes('SeamEvent')) { + resourceClasses.push('seam_event_from_dict') + } + const abstractClassName = `Abstract${cls.name}` const asyncClassName = `Async${cls.name}` const asyncAbstractClassName = `AbstractAsync${cls.name}` diff --git a/codegen/lib/python-type.ts b/codegen/lib/python-type.ts index 8e015a74..b711fc9b 100644 --- a/codegen/lib/python-type.ts +++ b/codegen/lib/python-type.ts @@ -15,7 +15,11 @@ type RecordValueType = NonNullable< export const mapParameterToPythonType = (parameter: Parameter): string => { if (parameter.format === 'list') { - return `List[${mapListItemFormatToPythonType(parameter.itemFormat)}]` + const itemType = + parameter.itemFormat === 'enum' + ? mapEnumToPythonType(parameter.itemEnumValues) + : mapListItemFormatToPythonType(parameter.itemFormat) + return `List[${itemType}]` } if (parameter.format === 'number') { @@ -30,6 +34,10 @@ export const mapParameterToPythonType = (parameter: Parameter): string => { return mapRecordToPythonType(parameter.valueTypes) } + if (parameter.format === 'enum') { + return mapEnumToPythonType(parameter.values) + } + return mapScalarFormatToPythonType(parameter.format) } @@ -54,9 +62,12 @@ export const mapRequiredPropertyToPythonType = ( nestedClassName?: string, ): string => { if (property.format === 'list') { - return `List[${ - nestedClassName ?? mapListItemFormatToPythonType(property.itemFormat) - }]` + if (nestedClassName != null) return `List[${nestedClassName}]` + const itemType = + property.itemFormat === 'enum' + ? mapEnumToPythonType(property.itemEnumValues) + : mapListItemFormatToPythonType(property.itemFormat) + return `List[${itemType}]` } if (property.format === 'number') { @@ -81,9 +92,20 @@ export const mapRequiredPropertyToPythonType = ( return nestedClassName } + if (property.format === 'enum') { + return mapEnumToPythonType(property.values) + } + return mapScalarFormatToPythonType(property.format) } +const mapEnumToPythonType = ( + values: Array<{ name: string }> | undefined, +): string => + values == null || values.length === 0 + ? 'str' + : `Literal[${values.map(({ name }) => JSON.stringify(name)).join(', ')}]` + const mapBooleanToPythonType = (values?: boolean[]): string => values == null || values.length === 0 ? 'bool' @@ -137,7 +159,11 @@ const mapScalarFormatToPythonType = (format: ScalarFormat): string => { } } -const mapListItemFormatToPythonType = (itemFormat: ListItemFormat): string => - itemFormat === 'discriminated_object' - ? 'Dict[str, Any]' - : mapScalarFormatToPythonType(itemFormat) +const mapListItemFormatToPythonType = (itemFormat: ListItemFormat): string => { + if (itemFormat === 'discriminated_object') { + throw new Error( + 'Discriminated object lists require generated variant classes.', + ) + } + return mapScalarFormatToPythonType(itemFormat) +} diff --git a/seam/modules/action_attempts.py b/seam/modules/action_attempts.py index 52ba420d..e87b0b59 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 +from ..resources import ActionAttempt, action_attempt_from_dict TIMEOUT = 5.0 POLLING_INTERVAL = 0.5 @@ -15,7 +15,7 @@ def get_action_attempt(client: SeamHttpClient, action_attempt_id: str) -> Action "/action_attempts/get", json={"action_attempt_id": action_attempt_id} ) - return ActionAttempt.from_dict(res["action_attempt"]) + return action_attempt_from_dict(res["action_attempt"]) def poll_until_ready( @@ -75,7 +75,7 @@ async def get_action_attempt_async( "/action_attempts/get", json={"action_attempt_id": action_attempt_id} ) - return ActionAttempt.from_dict(res["action_attempt"]) + return action_attempt_from_dict(res["action_attempt"]) async def poll_until_ready_async( diff --git a/seam/resources/__init__.py b/seam/resources/__init__.py index 50c2d161..a981676b 100644 --- a/seam/resources/__init__.py +++ b/seam/resources/__init__.py @@ -7,7 +7,31 @@ from .acs_entrance import AcsEntrance from .acs_system import AcsSystem from .acs_user import AcsUser -from .action_attempt import ActionAttempt +from .action_attempt import ( + LockDoorActionAttempt, + UnlockDoorActionAttempt, + ScanCredentialActionAttempt, + EncodeCredentialActionAttempt, + ScanToAssignCredentialActionAttempt, + AssignCredentialActionAttempt, + ResetSandboxWorkspaceActionAttempt, + SetFanModeActionAttempt, + SetHvacModeActionAttempt, + ActivateClimatePresetActionAttempt, + SimulateKeypadCodeEntryActionAttempt, + SimulateManualLockViaKeypadActionAttempt, + PushThermostatProgramsActionAttempt, + ConfigureAutoLockActionAttempt, + SyncAccessCodesActionAttempt, + CreateAccessCodeActionAttempt, + DeleteAccessCodeActionAttempt, + UpdateAccessCodeActionAttempt, + CreateNoiseThresholdActionAttempt, + DeleteNoiseThresholdActionAttempt, + UpdateNoiseThresholdActionAttempt, + ActionAttempt, + action_attempt_from_dict, +) from .batch import Batch from .client_session import ClientSession from .connect_webview import ConnectWebview @@ -19,7 +43,118 @@ from .noise_threshold import NoiseThreshold from .pagination import Pagination from .phone import Phone -from .seam_event import SeamEvent +from .seam_event import ( + AccessCodeCreatedEvent, + AccessCodeChangedEvent, + AccessCodeNameChangedEvent, + AccessCodeCodeChangedEvent, + AccessCodeTimeFrameChangedEvent, + AccessCodeMutationsRequestedEvent, + AccessCodeScheduledOnDeviceEvent, + AccessCodeSetOnDeviceEvent, + AccessCodeRemovedFromDeviceEvent, + AccessCodeDelayInSettingOnDeviceEvent, + AccessCodeFailedToSetOnDeviceEvent, + AccessCodeDeletedEvent, + AccessCodeDelayInRemovingFromDeviceEvent, + AccessCodeFailedToRemoveFromDeviceEvent, + AccessCodeModifiedExternalToSeamEvent, + AccessCodeDeletedExternalToSeamEvent, + AccessCodeBackupAccessCodePulledEvent, + AccessCodeUnmanagedConvertedToManagedEvent, + AccessCodeUnmanagedFailedToConvertToManagedEvent, + AccessCodeUnmanagedCreatedEvent, + AccessCodeUnmanagedRemovedEvent, + AccessGrantCreatedEvent, + AccessGrantDeletedEvent, + AccessGrantAccessGrantedToAllDoorsEvent, + AccessGrantAccessGrantedToDoorEvent, + AccessGrantAccessToDoorLostEvent, + AccessGrantAccessTimesChangedEvent, + AccessGrantCouldNotCreateRequestedAccessMethodsEvent, + AccessMethodIssuedEvent, + AccessMethodRevokedEvent, + AccessMethodCardEncodingRequiredEvent, + AccessMethodDeletedEvent, + AccessMethodReissuedEvent, + AccessMethodCreatedEvent, + AccessMethodDelayInIssuingEvent, + AccessMethodFailedToIssueEvent, + AcsSystemConnectedEvent, + AcsSystemAddedEvent, + AcsSystemDisconnectedEvent, + AcsCredentialDeletedEvent, + AcsCredentialIssuedEvent, + AcsCredentialReissuedEvent, + AcsCredentialInvalidatedEvent, + AcsUserCreatedEvent, + AcsUserDeletedEvent, + AcsEncoderAddedEvent, + AcsEncoderRemovedEvent, + AcsAccessGroupDeletedEvent, + AcsEntranceAddedEvent, + AcsEntranceRemovedEvent, + ClientSessionDeletedEvent, + ConnectedAccountConnectedEvent, + ConnectedAccountCreatedEvent, + ConnectedAccountSuccessfulLoginEvent, + ConnectedAccountDisconnectedEvent, + ConnectedAccountCompletedFirstSyncEvent, + ConnectedAccountDeletedEvent, + ConnectedAccountCompletedFirstSyncAfterReconnectionEvent, + ConnectedAccountReauthorizationRequestedEvent, + ActionAttemptLockDoorSucceededEvent, + ActionAttemptLockDoorFailedEvent, + ActionAttemptUnlockDoorSucceededEvent, + ActionAttemptUnlockDoorFailedEvent, + ActionAttemptSimulateKeypadCodeEntrySucceededEvent, + ActionAttemptSimulateKeypadCodeEntryFailedEvent, + ActionAttemptSimulateManualLockViaKeypadSucceededEvent, + ActionAttemptSimulateManualLockViaKeypadFailedEvent, + ConnectWebviewLoginSucceededEvent, + ConnectWebviewLoginFailedEvent, + DeviceConnectedEvent, + DeviceAddedEvent, + DeviceConvertedToUnmanagedEvent, + DeviceUnmanagedConvertedToManagedEvent, + DeviceUnmanagedConnectedEvent, + DeviceDisconnectedEvent, + DeviceUnmanagedDisconnectedEvent, + DeviceTamperedEvent, + DeviceLowBatteryEvent, + DeviceBatteryStatusChangedEvent, + DeviceRemovedEvent, + DeviceDeletedEvent, + DeviceThirdPartyIntegrationDetectedEvent, + DeviceThirdPartyIntegrationNoLongerDetectedEvent, + DeviceSaltoPrivacyModeActivatedEvent, + DeviceSaltoPrivacyModeDeactivatedEvent, + DeviceConnectionBecameFlakyEvent, + DeviceConnectionStabilizedEvent, + DeviceErrorSubscriptionRequiredEvent, + DeviceErrorSubscriptionRequiredResolvedEvent, + DeviceAccessoryKeypadConnectedEvent, + DeviceAccessoryKeypadDisconnectedEvent, + NoiseSensorNoiseThresholdTriggeredEvent, + LockLockedEvent, + LockUnlockedEvent, + LockAccessDeniedEvent, + ThermostatClimatePresetActivatedEvent, + ThermostatManuallyAdjustedEvent, + ThermostatTemperatureThresholdExceededEvent, + ThermostatTemperatureThresholdNoLongerExceededEvent, + ThermostatTemperatureReachedSetPointEvent, + ThermostatTemperatureChangedEvent, + DeviceNameChangedEvent, + CameraActivatedEvent, + DeviceDoorbellRangEvent, + PhoneDeactivatedEvent, + SpaceDeviceMembershipChangedEvent, + SpaceCreatedEvent, + SpaceDeletedEvent, + SeamEvent, + seam_event_from_dict, +) from .space import Space from .thermostat_daily_program import ThermostatDailyProgram from .thermostat_schedule import ThermostatSchedule diff --git a/seam/resources/access_code.py b/seam/resources/access_code.py index a971deee..8961c77f 100644 --- a/seam/resources/access_code.py +++ b/seam/resources/access_code.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AccessCode: """Represents a smart lock `access code `_. @@ -109,8 +116,8 @@ def from_dict(cls, d: Any): ) @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access code `_. + class ProviderIssueError(ResourceMapping): + """Indicates a provider-specific issue that prevents the access code from being set or managed. Check the error message for details. :ivar created_at: Date and time at which Seam created the error. @@ -119,55 +126,39 @@ class Errors(ResourceMapping): :ivar is_access_code_error: Indicates that this is an access code error. :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar managed_access_code_id: ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + created_at: Optional[str] + error_code: Literal["provider_issue"] + is_access_code_error: Literal[True] + message: str - :ivar unmanaged_access_code_id: ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) - :ivar change_type: Indicates the type of external modification. ``modified`` means the code's PIN or schedule was changed. ``removed`` means the code was deleted from the device. + @dataclass + class FailedToSetOnDeviceError(ResourceMapping): + """Failed to set code on device. - :ivar modified_fields: List of fields that were changed externally, with their previous and new values. + :ivar created_at: Date and time at which Seam created the error. - :ivar is_connected_account_error: + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar is_device_error: + :ivar is_access_code_error: Indicates that this is an access code error. - :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. """ - @dataclass - class ModifiedFields(ResourceMapping): - """List of fields that were changed externally, with their previous and new values. - - :ivar field: The name of the field that was changed (e.g. ``code``, ``starts_at``, ``ends_at``). - - :ivar from_: The previous value of the field. - - :ivar to: The new value of the field.""" - - field: str - from_: Optional[str] - to: Optional[str] - - @classmethod - def from_dict(cls, d: Any): - return cls( - field=d.get("field", None), - from_=d.get("from", None), - to=d.get("to", None), - ) - created_at: Optional[str] - error_code: str - is_access_code_error: Optional[Literal[True]] + error_code: Literal["failed_to_set_on_device"] + is_access_code_error: Literal[True] message: str - managed_access_code_id: Optional[str] - unmanaged_access_code_id: Optional[str] - change_type: Optional[str] - modified_fields: Optional[List[ModifiedFields]] - is_connected_account_error: Optional[bool] - is_device_error: Optional[Literal[False, True]] - is_bridge_error: Optional[bool] @classmethod def from_dict(cls, d: Any): @@ -176,119 +167,110 @@ def from_dict(cls, d: Any): error_code=d.get("error_code", None), is_access_code_error=d.get("is_access_code_error", None), message=d.get("message", None), - managed_access_code_id=d.get("managed_access_code_id", None), - unmanaged_access_code_id=d.get("unmanaged_access_code_id", None), - change_type=d.get("change_type", None), - modified_fields=[ - cls.ModifiedFields.from_dict(i) - for i in d.get("modified_fields") or [] - ], - is_connected_account_error=d.get("is_connected_account_error", None), - is_device_error=d.get("is_device_error", None), - is_bridge_error=d.get("is_bridge_error", None), ) @dataclass - class PendingMutations(ResourceMapping): - """Collection of pending mutations for the access code. Indicates changes that Seam is in the process of pushing to the device. + class FailedToRemoveFromDeviceError(ResourceMapping): + """Failed to remove code from device. - :ivar created_at: Date and time at which the mutation was created. + :ivar created_at: Date and time at which Seam created the error. - :ivar message: Detailed description of the mutation. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar mutation_code: + :ivar is_access_code_error: Indicates that this is an access code error. - :ivar scheduled_at: Date and time at which Seam will attempt to program this access code on the device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar from_: + created_at: Optional[str] + error_code: Literal["failed_to_remove_from_device"] + is_access_code_error: Literal[True] + message: str - :ivar to:""" + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) - @dataclass - class From(ResourceMapping): - """ + @dataclass + class DuplicateCodeOnDeviceError(ResourceMapping): + """Duplicate access code detected on device. - :ivar code: Previous PIN code. + :ivar created_at: Date and time at which Seam created the error. - :ivar name: Previous access code name. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar ends_at: Previous end time for the access code. + :ivar is_access_code_error: Indicates that this is an access code error. - :ivar starts_at: Previous start time for the access code.""" + :ivar managed_access_code_id: ID of the managed access code that conflicts with this managed access code, when Seam can identify it. - code: Optional[str] - name: Optional[str] - ends_at: Optional[str] - starts_at: Optional[str] + :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): - return cls( - code=d.get("code", None), - name=d.get("name", None), - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), - ) + :ivar unmanaged_access_code_id: ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + """ - @dataclass - class To(ResourceMapping): - """ + created_at: Optional[str] + error_code: Literal["duplicate_code_on_device"] + is_access_code_error: Literal[True] + managed_access_code_id: Optional[str] + message: str + unmanaged_access_code_id: Optional[str] - :ivar code: New PIN code. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + managed_access_code_id=d.get("managed_access_code_id", None), + message=d.get("message", None), + unmanaged_access_code_id=d.get("unmanaged_access_code_id", None), + ) - :ivar name: New access code name. + @dataclass + class NoSpaceForAccessCodeOnDeviceError(ResourceMapping): + """No space for access code on device. - :ivar ends_at: New end time for the access code. + :ivar created_at: Date and time at which Seam created the error. - :ivar starts_at: New start time for the access code.""" + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - code: Optional[str] - name: Optional[str] - ends_at: Optional[str] - starts_at: Optional[str] + :ivar is_access_code_error: Indicates that this is an access code error. - @classmethod - def from_dict(cls, d: Any): - return cls( - code=d.get("code", None), - name=d.get("name", None), - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), - ) + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - created_at: str + created_at: Optional[str] + error_code: Literal["no_space_for_access_code_on_device"] + is_access_code_error: Literal[True] message: str - mutation_code: str - scheduled_at: Optional[str] - from_: Optional[From] - to: Optional[To] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), message=d.get("message", None), - mutation_code=d.get("mutation_code", None), - scheduled_at=d.get("scheduled_at", None), - from_=( - cls.From.from_dict(d.get("from")) - if d.get("from") is not None - else None - ), - to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access code `_. + class ConflictingExternalModificationError(ResourceMapping): + """Code was modified or removed externally after Seam successfully set it on the device. The external change conflicts with the state that Seam is trying to apply, so Seam will attempt to set the code on the device again. - :ivar created_at: Date and time at which Seam created the warning. + :ivar change_type: Indicates the type of external modification. ``modified`` means the code's PIN or schedule was changed. ``removed`` means the code was deleted from the device. - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar created_at: Date and time at which Seam created the error. - :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar change_type: Indicates the type of external modification. ``modified`` means the code's PIN or schedule was changed. ``removed`` means the code was deleted from the device. + :ivar is_access_code_error: Indicates that this is an access code error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. :ivar modified_fields: List of fields that were changed externally, with their previous and new values. """ @@ -315,90 +297,1169 @@ def from_dict(cls, d: Any): to=d.get("to", None), ) + change_type: Optional[Literal["modified", "removed"]] created_at: Optional[str] + error_code: Literal["conflicting_external_modification"] + is_access_code_error: Literal[True] message: str - warning_code: str - change_type: Optional[str] modified_fields: Optional[List[ModifiedFields]] @classmethod def from_dict(cls, d: Any): return cls( + change_type=d.get("change_type", None), created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), - change_type=d.get("change_type", None), modified_fields=[ cls.ModifiedFields.from_dict(i) for i in d.get("modified_fields") or [] ], ) - access_code_id: str - code: Optional[str] - common_code_key: Optional[str] - created_at: str - device_id: str - dormakaba_oracode_metadata: Optional[DormakabaOracodeMetadata] - ends_at: Optional[str] - errors: List[Errors] - is_backup: Optional[bool] - is_backup_access_code_available: bool - is_external_modification_allowed: bool - is_managed: Literal[True] - is_offline_access_code: bool - is_one_time_use: bool - is_scheduled_on_device: Optional[bool] - is_waiting_for_code_assignment: Optional[bool] - name: Optional[str] - pending_mutations: List[PendingMutations] - pulled_backup_access_code_id: Optional[str] - starts_at: Optional[str] - status: str - type: str - warnings: List[Warnings] - workspace_id: str + @dataclass + class AccessCodeInactiveError(ResourceMapping): + """Indicates that the access code is disabled or inactive on the device. The code exists but will not grant access until re-enabled. - @classmethod - def from_dict(cls, d: Any): - return cls( - access_code_id=d.get("access_code_id", None), - code=d.get("code", None), - common_code_key=d.get("common_code_key", None), - created_at=d.get("created_at", None), - device_id=d.get("device_id", None), - dormakaba_oracode_metadata=( - cls.DormakabaOracodeMetadata.from_dict( - d.get("dormakaba_oracode_metadata") - ) - if d.get("dormakaba_oracode_metadata") is not None - else None - ), - ends_at=d.get("ends_at", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], - is_backup=d.get("is_backup", None), - is_backup_access_code_available=d.get( - "is_backup_access_code_available", None - ), - is_external_modification_allowed=d.get( - "is_external_modification_allowed", None - ), - is_managed=d.get("is_managed", None), - is_offline_access_code=d.get("is_offline_access_code", None), - is_one_time_use=d.get("is_one_time_use", None), - is_scheduled_on_device=d.get("is_scheduled_on_device", None), - is_waiting_for_code_assignment=d.get( - "is_waiting_for_code_assignment", None - ), - name=d.get("name", None), - pending_mutations=[ - cls.PendingMutations.from_dict(i) - for i in d.get("pending_mutations") or [] - ], - pulled_backup_access_code_id=d.get("pulled_backup_access_code_id", None), - starts_at=d.get("starts_at", None), - status=d.get("status", None), - type=d.get("type", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_access_code_error: Indicates that this is an access code error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: Optional[str] + error_code: Literal["access_code_inactive"] + is_access_code_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) + + @dataclass + class AccountDisconnectedError(ResourceMapping): + """Indicates that the account is disconnected. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["account_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitExceededError(ResourceMapping): + """Indicates that the Salto site user limit has been reached. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["salto_ks_subscription_limit_exceeded"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class InsufficientPermissionsError(ResourceMapping): + """Indicates that Seam's integration user does not have sufficient permissions on the provider's system to which this device belongs, so Seam cannot manage access codes or unlock the device. See the error message for specifics, then either reauthorize the connected account in Seam or grant the integration user the required permissions in the provider's system. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["insufficient_permissions"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DormakabaSitesDisconnectedError(ResourceMapping): + """Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["dormakaba_sites_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceOfflineError(ResourceMapping): + """Indicates that the device is offline. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["device_offline"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceRemovedError(ResourceMapping): + """Indicates that the device has been removed. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["device_removed"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class HubDisconnectedError(ResourceMapping): + """Indicates that the hub is disconnected. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["hub_disconnected"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceDisconnectedError(ResourceMapping): + """Indicates that the device is disconnected. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["device_disconnected"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class EmptyBackupAccessCodePoolError(ResourceMapping): + """Indicates that the `backup access code pool `_ is empty. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["empty_backup_access_code_pool"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class AugustLockNotAuthorizedError(ResourceMapping): + """Indicates that the user is not authorized to use the August lock. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["august_lock_not_authorized"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class MissingDeviceCredentialsError(ResourceMapping): + """Indicates that device credentials are missing. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["missing_device_credentials"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class AuxiliaryHeatRunningError(ResourceMapping): + """Indicates that the auxiliary heat is running. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["auxiliary_heat_running"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class SubscriptionRequiredError(ResourceMapping): + """Indicates that a subscription is required to connect. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["subscription_required"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class BridgeDisconnectedError(ResourceMapping): + """Indicates that the Seam API cannot communicate with `Seam Bridge `_, for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also `Troubleshooting Your Access Control System `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + + :ivar is_connected_account_error: Indicates whether the error is related specifically to the connected account. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["bridge_disconnected"] + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_bridge_error=d.get("is_bridge_error", None), + is_connected_account_error=d.get("is_connected_account_error", None), + message=d.get("message", None), + ) + + @dataclass + class CreatingPendingMutation(ResourceMapping): + """Seam is in the process of setting an access code on the device. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of setting an access code on the device. + """ + + created_at: str + message: str + mutation_code: Literal["creating"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + ) + + @dataclass + class DeferringCreationPendingMutation(ResourceMapping): + """Seam is waiting until closer to the access code's start time before programming it on the device. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is waiting until closer to the access code's start time before programming it on the device. + + :ivar scheduled_at: Date and time at which Seam will attempt to program this access code on the device. + """ + + created_at: str + message: str + mutation_code: Literal["deferring_creation"] + scheduled_at: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + scheduled_at=d.get("scheduled_at", None), + ) + + @dataclass + class DeletingPendingMutation(ResourceMapping): + """Seam is in the process of removing an access code from the device. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of removing an access code from the device. + """ + + created_at: str + message: str + mutation_code: Literal["deleting"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + ) + + @dataclass + class UpdatingCodePendingMutation(ResourceMapping): + """Seam is in the process of pushing an updated PIN code to the device. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous code configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing an updated PIN code to the device. + + :ivar to: New code configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous code configuration. + + :ivar code: Previous PIN code.""" + + code: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + code=d.get("code", None), + ) + + @dataclass + class To(ResourceMapping): + """New code configuration. + + :ivar code: New PIN code.""" + + code: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + code=d.get("code", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_code"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingNamePendingMutation(ResourceMapping): + """Seam is in the process of pushing an updated access code name to the device. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous name configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing an updated access code name to the device. + + :ivar to: New name configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous name configuration. + + :ivar name: Previous access code name.""" + + name: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + name=d.get("name", None), + ) + + @dataclass + class To(ResourceMapping): + """New name configuration. + + :ivar name: New access code name.""" + + name: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + name=d.get("name", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_name"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingTimeFramePendingMutation(ResourceMapping): + """Seam is in the process of pushing an updated time frame to the device. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous time frame configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated access code time frame to the device. + + :ivar to: New time frame configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous time frame configuration. + + :ivar ends_at: Previous end time for the access code. + + :ivar starts_at: Previous start time for the access code.""" + + ends_at: Optional[str] + starts_at: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + ends_at=d.get("ends_at", None), + starts_at=d.get("starts_at", None), + ) + + @dataclass + class To(ResourceMapping): + """New time frame configuration. + + :ivar ends_at: New end time for the access code. + + :ivar starts_at: New start time for the access code.""" + + ends_at: Optional[str] + starts_at: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + ends_at=d.get("ends_at", None), + starts_at=d.get("starts_at", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_time_frame"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class CodeRotatesPeriodicallyWarning(ResourceMapping): + """The access code's PIN rotates periodically when the code is renewed. Retrieve the latest code before each use. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["code_rotates_periodically"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TimeFrameAdjustedForUnknownTimeZoneWarning(ResourceMapping): + """The device's time zone is unknown and this code's time frame crosses a daylight-saving transition in at least one plausible time zone. A 1-hour safety buffer has been applied to the side of the time frame affected by the transition (``ends_at`` for spring-forward, ``starts_at`` for fall-back) so the code stays active through the shift — the code may be usable up to 1 hour beyond your requested window. Set the device's time zone via ``/devices/report_provider_metadata`` to clear the buffer and guarantee exact handling. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["time_frame_adjusted_for_unknown_time_zone"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ExternalModificationInEffectWarning(ResourceMapping): + """Code was modified or removed externally after Seam successfully set it on the device. External modification is allowed for this code, so the externally modified state is being honored. + + :ivar change_type: Indicates the type of external modification. ``modified`` means the code's PIN or schedule was changed. ``removed`` means the code was deleted from the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar modified_fields: List of fields that were changed externally, with their previous and new values. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + @dataclass + class ModifiedFields(ResourceMapping): + """List of fields that were changed externally, with their previous and new values. + + :ivar field: The name of the field that was changed (e.g. ``code``, ``starts_at``, ``ends_at``). + + :ivar from_: The previous value of the field. + + :ivar to: The new value of the field.""" + + field: str + from_: Optional[str] + to: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + field=d.get("field", None), + from_=d.get("from", None), + to=d.get("to", None), + ) + + change_type: Optional[Literal["modified", "removed"]] + created_at: Optional[str] + message: str + modified_fields: Optional[List[ModifiedFields]] + warning_code: Literal["external_modification_in_effect"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + change_type=d.get("change_type", None), + created_at=d.get("created_at", None), + message=d.get("message", None), + modified_fields=[ + cls.ModifiedFields.from_dict(i) + for i in d.get("modified_fields") or [] + ], + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DelayInSettingOnDeviceWarning(ResourceMapping): + """Delay in setting code on device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["delay_in_setting_on_device"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DelayInRemovingFromDeviceWarning(ResourceMapping): + """Delay in removing code from device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["delay_in_removing_from_device"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ThirdPartyIntegrationDetectedWarning(ResourceMapping): + """Third-party integration detected that may cause access codes to fail. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["third_party_integration_detected"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class IglooAlgopinMustBeUsedWithin24HoursWarning(ResourceMapping): + """Algopins must be used within 24 hours. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["igloo_algopin_must_be_used_within_24_hours"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ManagementTransferredWarning(ResourceMapping): + """Management was transferred to another workspace. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["management_transferred"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UsingBackupAccessCodeWarning(ResourceMapping): + """A backup access code has been pulled and is being used in place of this access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["using_backup_access_code"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class BeingDeletedWarning(ResourceMapping): + """Access code is being deleted. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithAccessCodeWarning(ResourceMapping): + """An unknown issue occurred with the access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["unknown_issue_with_access_code"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + Errors = Union[ + ProviderIssueError, + FailedToSetOnDeviceError, + FailedToRemoveFromDeviceError, + DuplicateCodeOnDeviceError, + NoSpaceForAccessCodeOnDeviceError, + ConflictingExternalModificationError, + AccessCodeInactiveError, + AccountDisconnectedError, + SaltoKsSubscriptionLimitExceededError, + InsufficientPermissionsError, + DormakabaSitesDisconnectedError, + DeviceOfflineError, + DeviceRemovedError, + HubDisconnectedError, + DeviceDisconnectedError, + EmptyBackupAccessCodePoolError, + AugustLockNotAuthorizedError, + MissingDeviceCredentialsError, + AuxiliaryHeatRunningError, + SubscriptionRequiredError, + BridgeDisconnectedError, + ] + _ErrorsVariants = { + "provider_issue": ProviderIssueError, + "failed_to_set_on_device": FailedToSetOnDeviceError, + "failed_to_remove_from_device": FailedToRemoveFromDeviceError, + "duplicate_code_on_device": DuplicateCodeOnDeviceError, + "no_space_for_access_code_on_device": NoSpaceForAccessCodeOnDeviceError, + "conflicting_external_modification": ConflictingExternalModificationError, + "access_code_inactive": AccessCodeInactiveError, + "account_disconnected": AccountDisconnectedError, + "salto_ks_subscription_limit_exceeded": SaltoKsSubscriptionLimitExceededError, + "insufficient_permissions": InsufficientPermissionsError, + "dormakaba_sites_disconnected": DormakabaSitesDisconnectedError, + "device_offline": DeviceOfflineError, + "device_removed": DeviceRemovedError, + "hub_disconnected": HubDisconnectedError, + "device_disconnected": DeviceDisconnectedError, + "empty_backup_access_code_pool": EmptyBackupAccessCodePoolError, + "august_lock_not_authorized": AugustLockNotAuthorizedError, + "missing_device_credentials": MissingDeviceCredentialsError, + "auxiliary_heat_running": AuxiliaryHeatRunningError, + "subscription_required": SubscriptionRequiredError, + "bridge_disconnected": BridgeDisconnectedError, + } + + PendingMutations = Union[ + CreatingPendingMutation, + DeferringCreationPendingMutation, + DeletingPendingMutation, + UpdatingCodePendingMutation, + UpdatingNamePendingMutation, + UpdatingTimeFramePendingMutation, + ] + _PendingMutationsVariants = { + "creating": CreatingPendingMutation, + "deferring_creation": DeferringCreationPendingMutation, + "deleting": DeletingPendingMutation, + "updating_code": UpdatingCodePendingMutation, + "updating_name": UpdatingNamePendingMutation, + "updating_time_frame": UpdatingTimeFramePendingMutation, + } + + Warnings = Union[ + CodeRotatesPeriodicallyWarning, + TimeFrameAdjustedForUnknownTimeZoneWarning, + ExternalModificationInEffectWarning, + DelayInSettingOnDeviceWarning, + DelayInRemovingFromDeviceWarning, + ThirdPartyIntegrationDetectedWarning, + IglooAlgopinMustBeUsedWithin24HoursWarning, + ManagementTransferredWarning, + UsingBackupAccessCodeWarning, + BeingDeletedWarning, + UnknownIssueWithAccessCodeWarning, + ] + _WarningsVariants = { + "code_rotates_periodically": CodeRotatesPeriodicallyWarning, + "time_frame_adjusted_for_unknown_time_zone": TimeFrameAdjustedForUnknownTimeZoneWarning, + "external_modification_in_effect": ExternalModificationInEffectWarning, + "delay_in_setting_on_device": DelayInSettingOnDeviceWarning, + "delay_in_removing_from_device": DelayInRemovingFromDeviceWarning, + "third_party_integration_detected": ThirdPartyIntegrationDetectedWarning, + "igloo_algopin_must_be_used_within_24_hours": IglooAlgopinMustBeUsedWithin24HoursWarning, + "management_transferred": ManagementTransferredWarning, + "using_backup_access_code": UsingBackupAccessCodeWarning, + "being_deleted": BeingDeletedWarning, + "unknown_issue_with_access_code": UnknownIssueWithAccessCodeWarning, + } + + access_code_id: str + code: Optional[str] + common_code_key: Optional[str] + created_at: str + device_id: str + dormakaba_oracode_metadata: Optional[DormakabaOracodeMetadata] + ends_at: Optional[str] + errors: List[Errors] + is_backup: Optional[bool] + is_backup_access_code_available: bool + is_external_modification_allowed: bool + is_managed: Literal[True] + is_offline_access_code: bool + is_one_time_use: bool + is_scheduled_on_device: Optional[bool] + is_waiting_for_code_assignment: Optional[bool] + name: Optional[str] + pending_mutations: List[PendingMutations] + pulled_backup_access_code_id: Optional[str] + starts_at: Optional[str] + status: Literal["setting", "set", "unset", "removing", "unknown"] + type: Literal["time_bound", "ongoing"] + warnings: List[Warnings] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + code=d.get("code", None), + common_code_key=d.get("common_code_key", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + dormakaba_oracode_metadata=( + cls.DormakabaOracodeMetadata.from_dict( + d.get("dormakaba_oracode_metadata") + ) + if d.get("dormakaba_oracode_metadata") is not None + else None + ), + ends_at=d.get("ends_at", None), + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], + is_backup=d.get("is_backup", None), + is_backup_access_code_available=d.get( + "is_backup_access_code_available", None + ), + is_external_modification_allowed=d.get( + "is_external_modification_allowed", None + ), + is_managed=d.get("is_managed", None), + is_offline_access_code=d.get("is_offline_access_code", None), + is_one_time_use=d.get("is_one_time_use", None), + is_scheduled_on_device=d.get("is_scheduled_on_device", None), + is_waiting_for_code_assignment=d.get( + "is_waiting_for_code_assignment", None + ), + name=d.get("name", None), + pending_mutations=[ + _from_discriminated_dict( + i, cls._PendingMutationsVariants, "mutation_code" + ) + for i in d.get("pending_mutations") or [] + ], + pulled_backup_access_code_id=d.get("pulled_backup_access_code_id", None), + starts_at=d.get("starts_at", None), + status=d.get("status", None), + type=d.get("type", None), + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/access_grant.py b/seam/resources/access_grant.py index 2b6c4345..9a1e5adc 100644 --- a/seam/resources/access_grant.py +++ b/seam/resources/access_grant.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AccessGrant: """Represents an Access Grant. Access Grants enable you to grant a user identity access to spaces, entrances, and devices through one or more access methods, such as mobile keys, plastic cards, and PIN codes. You can create an Access Grant for an existing user identity, or you can create a new user identity *while* creating the new Access Grant. @@ -49,8 +56,8 @@ class AccessGrant: :ivar workspace_id: ID of the Seam workspace associated with the Access Grant.""" @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access grant `_. + class CannotCreateRequestedAccessMethodsError(ResourceMapping): + """Indicates that Seam could not create one or more of the requested access methods for the access grant. :ivar created_at: Date and time at which Seam created the error. @@ -62,7 +69,7 @@ class Errors(ResourceMapping): """ created_at: str - error_code: str + error_code: Literal["cannot_create_requested_access_methods"] message: str missing_device_ids: Optional[List[str]] @@ -76,79 +83,134 @@ def from_dict(cls, d: Any): ) @dataclass - class PendingMutations(ResourceMapping): - """List of pending mutations for the access grant. This shows updates that are in progress. + class UpdatingSpacesPendingMutation(ResourceMapping): + """Seam is in the process of updating the devices/spaces associated with this access grant. :ivar created_at: Date and time at which the mutation was created. - :ivar from_: + :ivar from_: Previous location configuration. :ivar message: Detailed description of the mutation. - :ivar mutation_code: + :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. - :ivar to: - - :ivar access_method_ids: IDs of the access methods being updated.""" + :ivar to: New location configuration.""" @dataclass class From(ResourceMapping): - """ + """Previous location configuration. + + :ivar device_ids: Previous device IDs where access codes existed.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + @dataclass + class To(ResourceMapping): + """New location configuration. + + :ivar common_code_key: Common code key to ensure PIN code reuse across devices. + + :ivar device_ids: New device IDs where access codes should be created.""" + + common_code_key: Optional[str] + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + common_code_key=d.get("common_code_key", None), + device_ids=d.get("device_ids", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_spaces"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingAccessTimesPendingMutation(ResourceMapping): + """Seam is in the process of updating the access times for this access grant. + + :ivar access_method_ids: IDs of the access methods being updated. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous access time configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the access times for this access grant. - :ivar device_ids: Previous device IDs where access codes existed. + :ivar to: New access time configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous access time configuration. :ivar ends_at: Previous end time for access. :ivar starts_at: Previous start time for access.""" - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) @dataclass class To(ResourceMapping): - """ - - :ivar common_code_key: Common code key to ensure PIN code reuse across devices. - - :ivar device_ids: New device IDs where access codes should be created. + """New access time configuration. :ivar ends_at: New end time for access. :ivar starts_at: New start time for access.""" - common_code_key: Optional[str] - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - common_code_key=d.get("common_code_key", None), - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) + access_method_ids: List[str] created_at: str from_: Optional[From] message: str - mutation_code: str + mutation_code: Literal["updating_access_times"] to: Optional[To] - access_method_ids: Optional[List[str]] @classmethod def from_dict(cls, d: Any): return cls( + access_method_ids=d.get("access_method_ids", None), created_at=d.get("created_at", None), from_=( cls.From.from_dict(d.get("from")) @@ -158,7 +220,6 @@ def from_dict(cls, d: Any): message=d.get("message", None), mutation_code=d.get("mutation_code", None), to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, - access_method_ids=d.get("access_method_ids", None), ) @dataclass @@ -183,7 +244,7 @@ class RequestedAccessMethods(ResourceMapping): created_at: str display_name: str instant_key_max_use_count: Optional[int] - mode: str + mode: Literal["code", "card", "mobile_key", "cloud_key"] @classmethod def from_dict(cls, d: Any): @@ -197,26 +258,62 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access grant `_. + class BeingDeletedWarning(ResourceMapping): + """Indicates that the `access grant `_ is being deleted. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ - :ivar failed_devices: Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + created_at: str + message: str + warning_code: Literal["being_deleted"] - :ivar access_method_ids: IDs of the access methods being updated. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) - :ivar device_id: + @dataclass + class UnderprovisionedAccessWarning(ResourceMapping): + """Indicates that the access grant should have access to more locations than it currently does. Access methods are being created for the missing locations. - :ivar new_code: The new PIN code that was assigned instead. + :ivar created_at: Date and time at which Seam created the warning. - :ivar original_code: The originally requested PIN code that was unavailable. + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - :ivar reason: Specific reason why the grant's times are not programmable on the device. + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["underprovisioned_access"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class OverprovisionedAccessWarning(ResourceMapping): + """Indicates that the access grant has access to locations it should not have. Access methods are being removed from the extra locations. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar failed_devices: Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. """ @dataclass @@ -242,32 +339,176 @@ def from_dict(cls, d: Any): ) created_at: str - message: str - warning_code: str failed_devices: Optional[List[FailedDevices]] - access_method_ids: Optional[List[str]] - device_id: Optional[str] - new_code: Optional[str] - original_code: Optional[str] - reason: Optional[str] + message: str + warning_code: Literal["overprovisioned_access"] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), - message=d.get("message", None), - warning_code=d.get("warning_code", None), failed_devices=[ cls.FailedDevices.from_dict(i) for i in d.get("failed_devices") or [] ], + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UpdatingAccessTimesWarning(ResourceMapping): + """Indicates that the access times for this `access grant `_ are being updated. + + :ivar access_method_ids: IDs of the access methods being updated. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + access_method_ids: List[str] + created_at: str + message: str + warning_code: Literal["updating_access_times"] + + @classmethod + def from_dict(cls, d: Any): + return cls( access_method_ids=d.get("access_method_ids", None), + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class RequestedCodeUnavailableWarning(ResourceMapping): + """Indicates that the requested PIN code was already in use on a device, so a different code was assigned. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar device_id: ID of the device where the requested code was unavailable. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar new_code: The new PIN code that was assigned instead. + + :ivar original_code: The originally requested PIN code that was unavailable. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + device_id: str + message: str + new_code: str + original_code: str + warning_code: Literal["requested_code_unavailable"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), device_id=d.get("device_id", None), + message=d.get("message", None), new_code=d.get("new_code", None), original_code=d.get("original_code", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceDoesNotSupportAccessCodesWarning(ResourceMapping): + """Indicates that a device in the access grant does not support access codes and was excluded from code materialization. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar device_id: ID of the device that does not support access codes. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + device_id: str + message: str + warning_code: Literal["device_does_not_support_access_codes"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceTimeConstraintsViolatedWarning(ResourceMapping): + """Indicates that a device in the access grant cannot program an access code for the grant's time range because of device-specific time constraints. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar device_id: ID of the device whose time constraints the access grant violates. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar reason: Specific reason why the grant's times are not programmable on the device. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + device_id: str + message: str + reason: Literal[ + "duration_exceeds_max", "times_do_not_match_slots", "ongoing_not_supported" + ] + warning_code: Literal["device_time_constraints_violated"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + message=d.get("message", None), reason=d.get("reason", None), + warning_code=d.get("warning_code", None), ) + Errors = Union[CannotCreateRequestedAccessMethodsError] + _ErrorsVariants = { + "cannot_create_requested_access_methods": CannotCreateRequestedAccessMethodsError, + } + + PendingMutations = Union[ + UpdatingSpacesPendingMutation, UpdatingAccessTimesPendingMutation + ] + _PendingMutationsVariants = { + "updating_spaces": UpdatingSpacesPendingMutation, + "updating_access_times": UpdatingAccessTimesPendingMutation, + } + + Warnings = Union[ + BeingDeletedWarning, + UnderprovisionedAccessWarning, + OverprovisionedAccessWarning, + UpdatingAccessTimesWarning, + RequestedCodeUnavailableWarning, + DeviceDoesNotSupportAccessCodesWarning, + DeviceTimeConstraintsViolatedWarning, + ] + _WarningsVariants = { + "being_deleted": BeingDeletedWarning, + "underprovisioned_access": UnderprovisionedAccessWarning, + "overprovisioned_access": OverprovisionedAccessWarning, + "updating_access_times": UpdatingAccessTimesWarning, + "requested_code_unavailable": RequestedCodeUnavailableWarning, + "device_does_not_support_access_codes": DeviceDoesNotSupportAccessCodesWarning, + "device_time_constraints_violated": DeviceTimeConstraintsViolatedWarning, + } + access_grant_id: str access_grant_key: Optional[str] access_method_ids: List[str] @@ -300,12 +541,17 @@ def from_dict(cls, d: Any): customization_profile_id=d.get("customization_profile_id", None), display_name=d.get("display_name", None), ends_at=d.get("ends_at", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], instant_key_url=d.get("instant_key_url", None), location_ids=d.get("location_ids", None), name=d.get("name", None), pending_mutations=[ - cls.PendingMutations.from_dict(i) + _from_discriminated_dict( + i, cls._PendingMutationsVariants, "mutation_code" + ) for i in d.get("pending_mutations") or [] ], requested_access_methods=[ @@ -316,6 +562,9 @@ def from_dict(cls, d: Any): space_ids=d.get("space_ids", None), starts_at=d.get("starts_at", None), user_identity_id=d.get("user_identity_id", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/access_method.py b/seam/resources/access_method.py index b9e83c51..06e35d0a 100644 --- a/seam/resources/access_method.py +++ b/seam/resources/access_method.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AccessMethod: """Represents an access method for an Access Grant. Access methods describe the modes of access, such as PIN codes, plastic cards, and mobile keys. For a mobile key, the access method also stores the URL for the associated Instant Key. @@ -45,8 +52,8 @@ class AccessMethod: :ivar workspace_id: ID of the Seam workspace associated with the access method.""" @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access method `_. + class FailedToIssueError(ResourceMapping): + """Indicates that Seam was unable to issue this `access method `_ before its access grant started, so the recipient may be unable to access the space. This usually points to a problem that needs attention, such as an offline or disconnected device. Seam keeps retrying, and this error clears automatically if the access method is eventually issued. :ivar created_at: Date and time at which Seam created the error. @@ -56,7 +63,7 @@ class Errors(ResourceMapping): """ created_at: str - error_code: str + error_code: Literal["failed_to_issue"] message: str @classmethod @@ -68,59 +75,175 @@ def from_dict(cls, d: Any): ) @dataclass - class PendingMutations(ResourceMapping): - """Pending mutations for the `access method `_. Indicates operations that are in progress. + class ProvisioningAccessPendingMutation(ResourceMapping): + """Seam is in the process of provisioning access for this access method on new devices. :ivar created_at: Date and time at which the mutation was created. - :ivar from_: + :ivar from_: Previous device configuration. :ivar message: Detailed description of the mutation. - :ivar mutation_code: + :ivar mutation_code: Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. - :ivar to:""" + :ivar to: New device configuration.""" @dataclass class From(ResourceMapping): - """ + """Previous device configuration. + + :ivar device_ids: Previous device IDs where access was provisioned.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + @dataclass + class To(ResourceMapping): + """New device configuration. + + :ivar device_ids: New device IDs where access is being provisioned.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["provisioning_access"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class RevokingAccessPendingMutation(ResourceMapping): + """Seam is in the process of revoking access for this access method from devices. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous device configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of revoking access for this access method from devices. + + :ivar to: New device configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous device configuration. + + :ivar device_ids: Previous device IDs where access existed.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + @dataclass + class To(ResourceMapping): + """New device configuration. + + :ivar device_ids: New device IDs where access should remain.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["revoking_access"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingAccessTimesPendingMutation(ResourceMapping): + """Seam is in the process of updating the access times for this access method. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous access time configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the access times for this access method. + + :ivar to: New access time configuration.""" - :ivar device_ids: + @dataclass + class From(ResourceMapping): + """Previous access time configuration. :ivar ends_at: Previous end time for access. :ivar starts_at: Previous start time for access.""" - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) @dataclass class To(ResourceMapping): - """ - - :ivar device_ids: + """New access time configuration. :ivar ends_at: New end time for access. :ivar starts_at: New start time for access.""" - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) @@ -128,7 +251,7 @@ def from_dict(cls, d: Any): created_at: str from_: Optional[From] message: str - mutation_code: str + mutation_code: Literal["updating_access_times"] to: Optional[To] @classmethod @@ -146,32 +269,130 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access method `_. + class BeingDeletedWarning(ResourceMapping): + """Indicates that the `access method `_ is being deleted. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UpdatingAccessTimesWarning(ResourceMapping): + """Indicates that the access times for this `access method `_ are being updated. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["updating_access_times"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class PulledBackupAccessCodeWarning(ResourceMapping): + """Indicates that all attempts to create an access code on this device before the start time failed and a backup access code was used to ensure access was provided in time. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar original_access_method_id: ID of the original access method from which this backup access method was split, if applicable. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. """ created_at: str message: str - warning_code: str original_access_method_id: Optional[str] + warning_code: Literal["pulled_backup_access_code"] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), original_access_method_id=d.get("original_access_method_id", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DelayInIssuingWarning(ResourceMapping): + """Indicates that Seam has not yet issued this `access method `_, even though its access grant is about to begin, so access may not be ready when the recipient arrives. Seam is still attempting to issue it, and this warning clears automatically once issuance succeeds. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["delay_in_issuing"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), ) + Errors = Union[FailedToIssueError] + _ErrorsVariants = { + "failed_to_issue": FailedToIssueError, + } + + PendingMutations = Union[ + ProvisioningAccessPendingMutation, + RevokingAccessPendingMutation, + UpdatingAccessTimesPendingMutation, + ] + _PendingMutationsVariants = { + "provisioning_access": ProvisioningAccessPendingMutation, + "revoking_access": RevokingAccessPendingMutation, + "updating_access_times": UpdatingAccessTimesPendingMutation, + } + + Warnings = Union[ + BeingDeletedWarning, + UpdatingAccessTimesWarning, + PulledBackupAccessCodeWarning, + DelayInIssuingWarning, + ] + _WarningsVariants = { + "being_deleted": BeingDeletedWarning, + "updating_access_times": UpdatingAccessTimesWarning, + "pulled_backup_access_code": PulledBackupAccessCodeWarning, + "delay_in_issuing": DelayInIssuingWarning, + } + access_method_id: str client_session_token: Optional[str] code: Optional[str] @@ -186,7 +407,7 @@ def from_dict(cls, d: Any): is_ready_for_assignment: Optional[bool] is_ready_for_encoding: Optional[bool] issued_at: Optional[str] - mode: str + mode: Literal["code", "card", "mobile_key", "cloud_key"] pending_mutations: List[PendingMutations] warnings: List[Warnings] workspace_id: str @@ -200,7 +421,10 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), customization_profile_id=d.get("customization_profile_id", None), display_name=d.get("display_name", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], instant_key_url=d.get("instant_key_url", None), is_assignment_required=d.get("is_assignment_required", None), is_encoding_required=d.get("is_encoding_required", None), @@ -210,9 +434,14 @@ def from_dict(cls, d: Any): issued_at=d.get("issued_at", None), mode=d.get("mode", None), pending_mutations=[ - cls.PendingMutations.from_dict(i) + _from_discriminated_dict( + i, cls._PendingMutationsVariants, "mutation_code" + ) for i in d.get("pending_mutations") or [] ], - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/acs_access_group.py b/seam/resources/acs_access_group.py index ee50739a..cf3cd29d 100644 --- a/seam/resources/acs_access_group.py +++ b/seam/resources/acs_access_group.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AcsAccessGroup: """Group that defines the entrances to which a set of users has access and, in some cases, the access schedule for these entrances and users. @@ -64,8 +71,8 @@ def from_dict(cls, d: Any): ) @dataclass - class Errors(ResourceMapping): - """Errors associated with the ``acs_access_group``. + class FailedToCreateOnAcsSystemError(ResourceMapping): + """Indicates that the `access group `_ was not created on the `access system `_. This is likely due to an internal unexpected error. Contact Seam `support `_. :ivar created_at: Date and time at which Seam created the error. @@ -75,7 +82,7 @@ class Errors(ResourceMapping): """ created_at: str - error_code: str + error_code: Literal["failed_to_create_on_acs_system"] message: str @classmethod @@ -87,105 +94,358 @@ def from_dict(cls, d: Any): ) @dataclass - class PendingMutations(ResourceMapping): - """Collection of pending mutations for the access group. Represents operations that have been requested but not yet completed on the integrated access system. + class CreatingPendingMutation(ResourceMapping): + """Seam is in the process of pushing an access group creation to the integrated access system. :ivar created_at: Date and time at which the mutation was created. :ivar message: Detailed description of the mutation. - :ivar mutation_code: + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing an access group creation to the integrated access system. + """ - :ivar from_: + created_at: str + message: str + mutation_code: Literal["creating"] - :ivar to: + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + ) - :ivar acs_user_id: ID of the user involved in the scheduled change. + @dataclass + class DeletingPendingMutation(ResourceMapping): + """Seam is in the process of pushing an access group deletion to the integrated access system. - :ivar variant: Whether the user is scheduled to be added to or removed from this access group. + :ivar created_at: Date and time at which the mutation was created. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing an access group deletion to the integrated access system. + """ + + created_at: str + message: str + mutation_code: Literal["deleting"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + ) + + @dataclass + class DeferringDeletionPendingMutation(ResourceMapping): + """This access group is scheduled for automatic deletion when its access window expires. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that this access group is scheduled for automatic deletion when its access window expires. """ + created_at: str + message: str + mutation_code: Literal["deferring_deletion"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + ) + + @dataclass + class UpdatingGroupInformationPendingMutation(ResourceMapping): + """Seam is in the process of pushing an access group information update to the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old access group information. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated access group information to the integrated access system. + + :ivar to: New access group information.""" + @dataclass class From(ResourceMapping): - """ + """Old access group information. - :ivar name: Name of the access group. + :ivar name: Name of the access group.""" - :ivar ends_at: Ending time for the access schedule. + name: Optional[str] - :ivar starts_at: Starting time for the access schedule. + @classmethod + def from_dict(cls, d: Any): + return cls( + name=d.get("name", None), + ) - :ivar acs_user_id: Old user ID. + @dataclass + class To(ResourceMapping): + """New access group information. - :ivar acs_entrance_id: Old entrance ID.""" + :ivar name: Name of the access group.""" name: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + name=d.get("name", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_group_information"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingAccessSchedulePendingMutation(ResourceMapping): + """Seam is in the process of pushing an access schedule update to the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old access schedule information. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated access schedule information to the integrated access system. + + :ivar to: New access schedule information.""" + + @dataclass + class From(ResourceMapping): + """Old access schedule information. + + :ivar ends_at: Ending time for the access schedule. + + :ivar starts_at: Starting time for the access schedule.""" + ends_at: Optional[str] starts_at: Optional[str] - acs_user_id: Optional[str] - acs_entrance_id: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - name=d.get("name", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), - acs_user_id=d.get("acs_user_id", None), - acs_entrance_id=d.get("acs_entrance_id", None), ) @dataclass class To(ResourceMapping): - """ - - :ivar name: Name of the access group. + """New access schedule information. :ivar ends_at: Ending time for the access schedule. - :ivar starts_at: Starting time for the access schedule. - - :ivar acs_user_id: New user ID. + :ivar starts_at: Starting time for the access schedule.""" - :ivar acs_entrance_id: New entrance ID.""" - - name: Optional[str] ends_at: Optional[str] starts_at: Optional[str] - acs_user_id: Optional[str] - acs_entrance_id: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - name=d.get("name", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), - acs_user_id=d.get("acs_user_id", None), - acs_entrance_id=d.get("acs_entrance_id", None), ) created_at: str + from_: Optional[From] message: str - mutation_code: str + mutation_code: Literal["updating_access_schedule"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingUserMembershipPendingMutation(ResourceMapping): + """Seam is in the process of pushing a user membership update to the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old user membership. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated user membership information to the integrated access system. + + :ivar to: New user membership.""" + + @dataclass + class From(ResourceMapping): + """Old user membership. + + :ivar acs_user_id: Old user ID.""" + + acs_user_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_user_id=d.get("acs_user_id", None), + ) + + @dataclass + class To(ResourceMapping): + """New user membership. + + :ivar acs_user_id: New user ID.""" + + acs_user_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_user_id=d.get("acs_user_id", None), + ) + + created_at: str from_: Optional[From] + message: str + mutation_code: Literal["updating_user_membership"] to: Optional[To] - acs_user_id: Optional[str] - variant: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), message=d.get("message", None), mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingEntranceMembershipPendingMutation(ResourceMapping): + """Seam is in the process of pushing an entrance membership update to the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old entrance membership. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated entrance membership information to the integrated access system. + + :ivar to: New entrance membership.""" + + @dataclass + class From(ResourceMapping): + """Old entrance membership. + + :ivar acs_entrance_id: Old entrance ID.""" + + acs_entrance_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_entrance_id=d.get("acs_entrance_id", None), + ) + + @dataclass + class To(ResourceMapping): + """New entrance membership. + + :ivar acs_entrance_id: New entrance ID.""" + + acs_entrance_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_entrance_id=d.get("acs_entrance_id", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_entrance_membership"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), from_=( cls.From.from_dict(d.get("from")) if d.get("from") is not None else None ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class DeferringUserMembershipUpdatePendingMutation(ResourceMapping): + """A scheduled user membership change is pending for this access group. + + :ivar acs_user_id: ID of the user involved in the scheduled change. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that a scheduled user membership change is pending for this access group. + + :ivar variant: Whether the user is scheduled to be added to or removed from this access group. + """ + + acs_user_id: str + created_at: str + message: str + mutation_code: Literal["deferring_user_membership_update"] + variant: Literal["adding", "removing"] + + @classmethod + def from_dict(cls, d: Any): + return cls( acs_user_id=d.get("acs_user_id", None), + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), variant=d.get("variant", None), ) @@ -202,7 +462,7 @@ class Warnings(ResourceMapping): created_at: str message: str - warning_code: str + warning_code: Literal["unknown_issue_with_acs_access_group", "being_deleted"] @classmethod def from_dict(cls, d: Any): @@ -212,7 +472,44 @@ def from_dict(cls, d: Any): warning_code=d.get("warning_code", None), ) - access_group_type: str + Errors = Union[FailedToCreateOnAcsSystemError] + _ErrorsVariants = { + "failed_to_create_on_acs_system": FailedToCreateOnAcsSystemError, + } + + PendingMutations = Union[ + CreatingPendingMutation, + DeletingPendingMutation, + DeferringDeletionPendingMutation, + UpdatingGroupInformationPendingMutation, + UpdatingAccessSchedulePendingMutation, + UpdatingUserMembershipPendingMutation, + UpdatingEntranceMembershipPendingMutation, + DeferringUserMembershipUpdatePendingMutation, + ] + _PendingMutationsVariants = { + "creating": CreatingPendingMutation, + "deleting": DeletingPendingMutation, + "deferring_deletion": DeferringDeletionPendingMutation, + "updating_group_information": UpdatingGroupInformationPendingMutation, + "updating_access_schedule": UpdatingAccessSchedulePendingMutation, + "updating_user_membership": UpdatingUserMembershipPendingMutation, + "updating_entrance_membership": UpdatingEntranceMembershipPendingMutation, + "deferring_user_membership_update": DeferringUserMembershipUpdatePendingMutation, + } + + access_group_type: Literal[ + "pti_unit", + "pti_access_level", + "salto_ks_access_group", + "brivo_group", + "salto_space_group", + "dormakaba_community_access_group", + "dormakaba_ambiance_access_group", + "avigilon_alta_group", + "kisi_access_group", + "akiles_member_group", + ] access_group_type_display_name: str access_schedule: Optional[AccessSchedule] acs_access_group_id: str @@ -221,7 +518,18 @@ def from_dict(cls, d: Any): created_at: str display_name: str errors: List[Errors] - external_type: str + external_type: Literal[ + "pti_unit", + "pti_access_level", + "salto_ks_access_group", + "brivo_group", + "salto_space_group", + "dormakaba_community_access_group", + "dormakaba_ambiance_access_group", + "avigilon_alta_group", + "kisi_access_group", + "akiles_member_group", + ] external_type_display_name: str is_managed: Literal[True] name: str @@ -246,13 +554,18 @@ def from_dict(cls, d: Any): connected_account_id=d.get("connected_account_id", None), created_at=d.get("created_at", None), display_name=d.get("display_name", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], external_type=d.get("external_type", None), external_type_display_name=d.get("external_type_display_name", None), is_managed=d.get("is_managed", None), name=d.get("name", None), pending_mutations=[ - cls.PendingMutations.from_dict(i) + _from_discriminated_dict( + i, cls._PendingMutationsVariants, "mutation_code" + ) for i in d.get("pending_mutations") or [] ], warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], diff --git a/seam/resources/acs_credential.py b/seam/resources/acs_credential.py index 2baaad9f..5b916f38 100644 --- a/seam/resources/acs_credential.py +++ b/seam/resources/acs_credential.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AcsCredential: """Means by which an `access control system user `_ gains access at an `entrance `_. The ``acs_credential`` object represents a `credential `_ that provides an ACS user access within an `access control system `_. @@ -168,7 +175,7 @@ class VisionlineMetadata(ResourceMapping): """ auto_join: Optional[bool] - card_function_type: Optional[str] + card_function_type: Optional[Literal["guest", "staff"]] card_id: Optional[str] common_acs_entrance_ids: Optional[List[str]] credential_id: Optional[str] @@ -190,37 +197,194 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `credential `_. + class WaitingToBeIssuedWarning(ResourceMapping): + """Indicates that the `credential `_ is waiting to be issued. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["waiting_to_be_issued"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ScheduleExternallyModifiedWarning(ResourceMapping): + """Indicates that the schedule of one of the `credential `_'s children was modified externally. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["schedule_externally_modified"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ScheduleModifiedWarning(ResourceMapping): + """Indicates that the schedule of the `credential `_ was modified to avoid creating a credential with a start date in the past. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["schedule_modified"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class BeingDeletedWarning(ResourceMapping): + """Indicates that the `credential `_ is being deleted. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithAcsCredentialWarning(ResourceMapping): + """An unknown issue occurred while syncing the state of the `credential `_ with the provider. This issue may affect the proper functioning of the credential. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["unknown_issue_with_acs_credential"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class NeedsToBeReissuedWarning(ResourceMapping): + """Access permissions for the `credential `_ have changed. `Reissue `_ (re-encode) the credential. This issue may affect the proper functioning of the credential. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["needs_to_be_reissued"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class RequestedCodeUnavailableWarning(ResourceMapping): + """Indicates that the requested PIN code could not be used, so the access system assigned a different code. Give the guest the assigned code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar new_code: The PIN code that was assigned instead. :ivar original_code: The originally requested PIN code that could not be used. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. """ created_at: str message: str - warning_code: str - new_code: Optional[str] - original_code: Optional[str] + new_code: str + original_code: str + warning_code: Literal["requested_code_unavailable"] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), new_code=d.get("new_code", None), original_code=d.get("original_code", None), + warning_code=d.get("warning_code", None), ) - access_method: str + Warnings = Union[ + WaitingToBeIssuedWarning, + ScheduleExternallyModifiedWarning, + ScheduleModifiedWarning, + BeingDeletedWarning, + UnknownIssueWithAcsCredentialWarning, + NeedsToBeReissuedWarning, + RequestedCodeUnavailableWarning, + ] + _WarningsVariants = { + "waiting_to_be_issued": WaitingToBeIssuedWarning, + "schedule_externally_modified": ScheduleExternallyModifiedWarning, + "schedule_modified": ScheduleModifiedWarning, + "being_deleted": BeingDeletedWarning, + "unknown_issue_with_acs_credential": UnknownIssueWithAcsCredentialWarning, + "needs_to_be_reissued": NeedsToBeReissuedWarning, + "requested_code_unavailable": RequestedCodeUnavailableWarning, + } + + access_method: Literal["code", "card", "mobile_key", "cloud_key"] acs_credential_id: str acs_credential_pool_id: Optional[str] acs_system_id: str @@ -234,7 +398,24 @@ def from_dict(cls, d: Any): display_name: str ends_at: Optional[str] errors: List[Errors] - external_type: Optional[str] + external_type: Optional[ + Literal[ + "pti_card", + "brivo_credential", + "hid_credential", + "visionline_card", + "salto_ks_credential", + "assa_abloy_vostio_key", + "salto_space_key", + "latch_access", + "dormakaba_ambiance_credential", + "hotek_card", + "salto_ks_tag", + "avigilon_alta_credential", + "kisi_credential", + "akiles_credential", + ] + ] external_type_display_name: Optional[str] is_issued: Optional[bool] is_latest_desired_state_synced_with_provider: Optional[bool] @@ -300,6 +481,9 @@ def from_dict(cls, d: Any): if d.get("visionline_metadata") is not None else None ), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/acs_encoder.py b/seam/resources/acs_encoder.py index e1f52f93..510c6b71 100644 --- a/seam/resources/acs_encoder.py +++ b/seam/resources/acs_encoder.py @@ -48,7 +48,7 @@ class Errors(ResourceMapping): """ created_at: str - error_code: str + error_code: Literal["acs_encoder_removed"] message: str @classmethod diff --git a/seam/resources/acs_entrance.py b/seam/resources/acs_entrance.py index 148c3ba6..1c2a3661 100644 --- a/seam/resources/acs_entrance.py +++ b/seam/resources/acs_entrance.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AcsEntrance: """Represents an `entrance `_ within an `access control system `_. @@ -122,7 +129,9 @@ class AssaAbloyVostioMetadata(ResourceMapping): door_name: Optional[str] door_number: Optional[float] - door_type: Optional[str] + door_type: Optional[ + Literal["CommonDoor", "EntranceDoor", "GuestDoor", "Elevator"] + ] pms_id: Optional[str] stand_open: Optional[bool] @@ -393,7 +402,9 @@ class Profiles(ResourceMapping): """ visionline_door_profile_id: Optional[str] - visionline_door_profile_type: Optional[str] + visionline_door_profile_type: Optional[ + Literal["BLE", "commonDoor", "touch"] + ] @classmethod def from_dict(cls, d: Any): @@ -406,7 +417,9 @@ def from_dict(cls, d: Any): ), ) - door_category: Optional[str] + door_category: Optional[ + Literal["entrance", "guest", "elevator reader", "common", "common (PMS)"] + ] door_name: Optional[str] profiles: Optional[List[Profiles]] @@ -419,8 +432,77 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `entrance `_. + class SaltoKsEntranceAccessCodeSupportRemovedWarning(ResourceMapping): + """Indicates that a change in the reported device model has been detected for this Salto KS entrance, which may occur after an IQ hub reset. Access code support may be affected. See https://help.getseam.com/articles/5098842588-salto-ks-lock-loses-access-code-support for troubleshooting steps. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_entrance_access_code_support_removed"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class EntranceSharesZoneWarning(ResourceMapping): + """Indicates that this entrance shares a zone with other entrances in Avigilon Alta and cannot be added to an access group individually. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["entrance_shares_zone"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class EntranceSetupRequiredWarning(ResourceMapping): + """Indicates that this entrance requires additional configuration in the access control system before Seam can fully manage it. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["entrance_setup_required"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsPrivacyModeWarning(ResourceMapping): + """Indicates that this entrance is in privacy mode. When privacy mode is enabled, access codes, mobile keys, and remote unlocks will not work unless the user has admin access. :ivar created_at: Date and time at which Seam created the warning. @@ -431,7 +513,7 @@ class Warnings(ResourceMapping): created_at: str message: str - warning_code: str + warning_code: Literal["salto_ks_privacy_mode"] @classmethod def from_dict(cls, d: Any): @@ -441,6 +523,44 @@ def from_dict(cls, d: Any): warning_code=d.get("warning_code", None), ) + @dataclass + class PrivacyModeWarning(ResourceMapping): + """Indicates that this entrance is in privacy mode. When privacy mode is enabled, access codes, mobile keys, and remote unlocks will not work unless the user has admin access. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["privacy_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + Warnings = Union[ + SaltoKsEntranceAccessCodeSupportRemovedWarning, + EntranceSharesZoneWarning, + EntranceSetupRequiredWarning, + SaltoKsPrivacyModeWarning, + PrivacyModeWarning, + ] + _WarningsVariants = { + "salto_ks_entrance_access_code_support_removed": SaltoKsEntranceAccessCodeSupportRemovedWarning, + "entrance_shares_zone": EntranceSharesZoneWarning, + "entrance_setup_required": EntranceSetupRequiredWarning, + "salto_ks_privacy_mode": SaltoKsPrivacyModeWarning, + "privacy_mode": PrivacyModeWarning, + } + acs_entrance_id: str acs_system_id: str akiles_metadata: Optional[AkilesMetadata] @@ -544,5 +664,8 @@ def from_dict(cls, d: Any): if d.get("visionline_metadata") is not None else None ), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], ) diff --git a/seam/resources/acs_system.py b/seam/resources/acs_system.py index 0ae9b83f..efe0bf50 100644 --- a/seam/resources/acs_system.py +++ b/seam/resources/acs_system.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AcsSystem: """Represents an `access control system `_. @@ -54,30 +61,219 @@ class AcsSystem: """ @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access control system `_. + class SeamBridgeDisconnectedError(ResourceMapping): + """Indicates that the Seam API cannot communicate with `Seam Bridge `_, for example, if Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. + This error might also occur if Seam Bridge is connected to the wrong `workspace `_. + See also `Troubleshooting Your Access Control System `_. :ivar created_at: Date and time at which Seam created the error. :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["seam_bridge_disconnected"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class BridgeDisconnectedError(ResourceMapping): + """Indicates that the Seam API cannot communicate with `Seam Bridge `_, for example, if Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. + See also `Troubleshooting Your Access Control System `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. :ivar is_bridge_error: Indicates whether the error is related to the `Seam Bridge `_. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. """ created_at: str - error_code: str - message: str + error_code: Literal["bridge_disconnected"] is_bridge_error: Optional[bool] + message: str @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), error_code=d.get("error_code", None), - message=d.get("message", None), is_bridge_error=d.get("is_bridge_error", None), + message=d.get("message", None), + ) + + @dataclass + class VisionlineInstanceUnreachableError(ResourceMapping): + """Indicates that `Seam Bridge `_ is functioning correctly and the Seam API can communicate with Seam Bridge, but the Seam API cannot connect to the on-premises `Visionline access control system `_. + For example, the IP address of the on-premises access control system may be set incorrectly within the Seam `workspace `_. + See also `Troubleshooting Your Access Control System `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["visionline_instance_unreachable"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitExceededError(ResourceMapping): + """Indicates that the maximum number of users allowed for the site has been reached. This means that new access codes cannot be created. Contact Salto support to increase the user limit. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["salto_ks_subscription_limit_exceeded"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class InsufficientPermissionsError(ResourceMapping): + """Indicates that Seam's integration user does not have sufficient permissions on the provider's system backing this `access control system `_. Access cannot be managed until permissions are restored. See the error message for specifics, then either reauthorize the connected account in Seam or grant the integration user the required permissions in the provider's system. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["insufficient_permissions"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AcsSystemDisconnectedError(ResourceMapping): + """Indicates that the `access control system `_ has been disconnected. See `Troubleshooting Your Access Control System `_ to resolve the issue. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["acs_system_disconnected"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AccountDisconnectedError(ResourceMapping): + """Indicates that the login credentials are invalid. Reconnect the account using a `Connect Webview `_ to restore access. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["account_disconnected"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class SaltoKsCertificationExpiredError(ResourceMapping): + """Indicates that the `access control system `_ has lost its Salto KS certification. Contact `support `_ to regain access. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["salto_ks_certification_expired"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ProviderServiceUnavailableError(ResourceMapping): + """Indicates that the access control system provider's service is temporarily unavailable. Seam will automatically retry and reconnect when the service becomes available again. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["provider_service_unavailable"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), ) @dataclass @@ -119,33 +315,139 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access control system `_. + class SaltoKsSubscriptionLimitAlmostReachedWarning(ResourceMapping): + """Indicates that the Salto KS site has exceeded 80% of the maximum number of allowed users. Increase your subscription limit or delete some users from your site to rectify the issue. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ - :ivar misconfigured_acs_entrance_ids: Deprecated: this field is deprecated.""" + created_at: str + message: str + warning_code: Literal["salto_ks_subscription_limit_almost_reached"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TimeZoneDoesNotMatchLocationWarning(ResourceMapping): + """Indicates the `access control system `_ time zone could not be determined because the reported physical location does not match the time zone configured on the physical `ACS entrances `_. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar misconfigured_acs_entrance_ids: Deprecated: this field is deprecated. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ created_at: str message: str - warning_code: str misconfigured_acs_entrance_ids: Optional[List[str]] + warning_code: Literal["time_zone_does_not_match_location"] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), misconfigured_acs_entrance_ids=d.get( "misconfigured_acs_entrance_ids", None ), + warning_code=d.get("warning_code", None), ) + @dataclass + class SetupRequiredWarning(ResourceMapping): + """Indicates that the access control system requires additional setup before it can be fully operational. Follow the instructions in the warning message to complete the setup. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["setup_required"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithAcsSystemWarning(ResourceMapping): + """Indicates that Seam encountered an unexpected error while syncing this `access control system `_, so its users, credentials, and access groups may be out of date. Seam retries on every sync cycle and clears this warning once a sync succeeds; if it persists, contact `support `_. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["unknown_issue_with_acs_system"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + Errors = Union[ + SeamBridgeDisconnectedError, + BridgeDisconnectedError, + VisionlineInstanceUnreachableError, + SaltoKsSubscriptionLimitExceededError, + InsufficientPermissionsError, + AcsSystemDisconnectedError, + AccountDisconnectedError, + SaltoKsCertificationExpiredError, + ProviderServiceUnavailableError, + ] + _ErrorsVariants = { + "seam_bridge_disconnected": SeamBridgeDisconnectedError, + "bridge_disconnected": BridgeDisconnectedError, + "visionline_instance_unreachable": VisionlineInstanceUnreachableError, + "salto_ks_subscription_limit_exceeded": SaltoKsSubscriptionLimitExceededError, + "insufficient_permissions": InsufficientPermissionsError, + "acs_system_disconnected": AcsSystemDisconnectedError, + "account_disconnected": AccountDisconnectedError, + "salto_ks_certification_expired": SaltoKsCertificationExpiredError, + "provider_service_unavailable": ProviderServiceUnavailableError, + } + + Warnings = Union[ + SaltoKsSubscriptionLimitAlmostReachedWarning, + TimeZoneDoesNotMatchLocationWarning, + SetupRequiredWarning, + UnknownIssueWithAcsSystemWarning, + ] + _WarningsVariants = { + "salto_ks_subscription_limit_almost_reached": SaltoKsSubscriptionLimitAlmostReachedWarning, + "time_zone_does_not_match_location": TimeZoneDoesNotMatchLocationWarning, + "setup_required": SetupRequiredWarning, + "unknown_issue_with_acs_system": UnknownIssueWithAcsSystemWarning, + } + acs_access_group_count: Optional[float] acs_system_id: str acs_user_count: Optional[float] @@ -154,14 +456,54 @@ def from_dict(cls, d: Any): created_at: str default_credential_manager_acs_system_id: Optional[str] errors: List[Errors] - external_type: Optional[str] + external_type: Optional[ + Literal[ + "pti_site", + "avigilon_alta_org", + "salto_ks_site", + "salto_space_system", + "brivo_account", + "hid_credential_manager_organization", + "visionline_system", + "assa_abloy_credential_service", + "latch_building", + "dormakaba_community_site", + "dormakaba_ambiance_site", + "legic_connect_credential_service", + "assa_abloy_vostio", + "assa_abloy_vostio_credential_service", + "hotek_site", + "kisi_organization", + "akiles_organization", + ] + ] external_type_display_name: Optional[str] image_alt_text: str image_url: str is_credential_manager: bool location: Optional[Location] name: str - system_type: Optional[str] + system_type: Optional[ + Literal[ + "pti_site", + "avigilon_alta_org", + "salto_ks_site", + "salto_space_system", + "brivo_account", + "hid_credential_manager_organization", + "visionline_system", + "assa_abloy_credential_service", + "latch_building", + "dormakaba_community_site", + "dormakaba_ambiance_site", + "legic_connect_credential_service", + "assa_abloy_vostio", + "assa_abloy_vostio_credential_service", + "hotek_site", + "kisi_organization", + "akiles_organization", + ] + ] system_type_display_name: Optional[str] visionline_metadata: Optional[VisionlineMetadata] warnings: List[Warnings] @@ -179,7 +521,10 @@ def from_dict(cls, d: Any): default_credential_manager_acs_system_id=d.get( "default_credential_manager_acs_system_id", None ), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], external_type=d.get("external_type", None), external_type_display_name=d.get("external_type_display_name", None), image_alt_text=d.get("image_alt_text", None), @@ -198,6 +543,9 @@ def from_dict(cls, d: Any): if d.get("visionline_metadata") is not None else None ), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/acs_user.py b/seam/resources/acs_user.py index 4c1ffe44..5ac83c10 100644 --- a/seam/resources/acs_user.py +++ b/seam/resources/acs_user.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class AcsUser: """Represents a `user `_ in an `access system `_. @@ -83,8 +90,100 @@ def from_dict(cls, d: Any): ) @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access system user `_. + class DeletedExternallyError(ResourceMapping): + """Indicates that the `access system user `_ was deleted from the `access system `_ outside of Seam. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["deleted_externally"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitExceededError(ResourceMapping): + """Indicates that the `access system user `_ could not be subscribed on Salto KS because the subscription limit has been exceeded. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["salto_ks_subscription_limit_exceeded"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class FailedToCreateOnAcsSystemError(ResourceMapping): + """Indicates that the `access system user `_ was not created on the `access system `_. This is likely due to an internal unexpected error. Contact Seam `support `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["failed_to_create_on_acs_system"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class FailedToUpdateOnAcsSystemError(ResourceMapping): + """Indicates that the `access system user `_ was not updated on the `access system `_. This is likely due to an internal unexpected error. Contact Seam `support `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["failed_to_update_on_acs_system"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class FailedToDeleteOnAcsSystemError(ResourceMapping): + """Indicates that the `access system user `_ was not deleted on the `access system `_. This is likely due to an internal unexpected error. Contact Seam `support `_. :ivar created_at: Date and time at which Seam created the error. @@ -94,7 +193,7 @@ class Errors(ResourceMapping): """ created_at: str - error_code: str + error_code: Literal["failed_to_delete_on_acs_system"] message: str @classmethod @@ -106,54 +205,127 @@ def from_dict(cls, d: Any): ) @dataclass - class PendingMutations(ResourceMapping): - """Pending mutations associated with the `access system user `_. Seam is in the process of pushing these mutations to the integrated access system. + class LatchConflictWithResidentUserError(ResourceMapping): + """Indicates that the `access system user `_ was created from the Seam API but also exists on Mission Control. This is unsupported. Contact Seam `support `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["latch_conflict_with_resident_user"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class CreatingPendingMutation(ResourceMapping): + """Seam is in the process of pushing a user creation to the integrated access system. :ivar created_at: Date and time at which the mutation was created. :ivar message: Detailed description of the mutation. - :ivar mutation_code: + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing a user creation to the integrated access system. + """ - :ivar scheduled_at: Optional: When the user creation is scheduled to occur. + created_at: str + message: str + mutation_code: Literal["creating"] - :ivar from_: + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + ) - :ivar to: + @dataclass + class DeletingPendingMutation(ResourceMapping): + """Seam is in the process of pushing a user deletion to the integrated access system. - :ivar acs_access_group_id: ID of the access group involved in the scheduled change. + :ivar created_at: Date and time at which the mutation was created. - :ivar variant: Whether the user is scheduled to be added to or removed from the access group. + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing a user deletion to the integrated access system. """ - @dataclass - class From(ResourceMapping): - """ + created_at: str + message: str + mutation_code: Literal["deleting"] - :ivar email_address: Email address of the access system user. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + ) - :ivar full_name: Full name of the access system user. + @dataclass + class DeferringCreationPendingMutation(ResourceMapping): + """User exists in Seam but has not been pushed to the provider yet. Will be created when a credential is issued. - :ivar phone_number: Phone number of the access system user. + :ivar created_at: Date and time at which the mutation was created. - :ivar ends_at: Starting time for the access schedule. + :ivar message: Detailed description of the mutation. - :ivar starts_at: Starting time for the access schedule. + :ivar mutation_code: Mutation code to indicate that Seam is intentionally deferring the creation of the user on the access control system until the appropriate time. - :ivar is_suspended: + :ivar scheduled_at: Optional: When the user creation is scheduled to occur.""" - :ivar acs_access_group_id: Old access group ID. + created_at: str + message: str + mutation_code: Literal["deferring_creation"] + scheduled_at: Optional[str] - :ivar acs_credential_id: Previous credential ID.""" + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + scheduled_at=d.get("scheduled_at", None), + ) + + @dataclass + class UpdatingUserInformationPendingMutation(ResourceMapping): + """ + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old access system user information. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated user information to the integrated access system. + + :ivar to: New access system user information.""" + + @dataclass + class From(ResourceMapping): + """Old access system user information. + + :ivar email_address: Email address of the access system user. + + :ivar full_name: Full name of the access system user. + + :ivar phone_number: Phone number of the access system user.""" email_address: Optional[str] full_name: Optional[str] phone_number: Optional[str] - ends_at: Optional[str] - starts_at: Optional[str] - is_suspended: Optional[bool] - acs_access_group_id: Optional[str] - acs_credential_id: Optional[str] @classmethod def from_dict(cls, d: Any): @@ -161,81 +333,337 @@ def from_dict(cls, d: Any): email_address=d.get("email_address", None), full_name=d.get("full_name", None), phone_number=d.get("phone_number", None), - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), - is_suspended=d.get("is_suspended", None), - acs_access_group_id=d.get("acs_access_group_id", None), - acs_credential_id=d.get("acs_credential_id", None), ) @dataclass class To(ResourceMapping): - """ + """New access system user information. :ivar email_address: Email address of the access system user. :ivar full_name: Full name of the access system user. - :ivar phone_number: Phone number of the access system user. + :ivar phone_number: Phone number of the access system user.""" + + email_address: Optional[str] + full_name: Optional[str] + phone_number: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + email_address=d.get("email_address", None), + full_name=d.get("full_name", None), + phone_number=d.get("phone_number", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_user_information"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingAccessSchedulePendingMutation(ResourceMapping): + """Seam is in the process of pushing an access schedule update to the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old access schedule information. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated access schedule information to the integrated access system. + + :ivar to: New access schedule information.""" + + @dataclass + class From(ResourceMapping): + """Old access schedule information. :ivar ends_at: Starting time for the access schedule. - :ivar starts_at: Starting time for the access schedule. + :ivar starts_at: Starting time for the access schedule.""" - :ivar is_suspended: + ends_at: Optional[str] + starts_at: Optional[str] - :ivar acs_access_group_id: New access group ID. + @classmethod + def from_dict(cls, d: Any): + return cls( + ends_at=d.get("ends_at", None), + starts_at=d.get("starts_at", None), + ) - :ivar acs_credential_id: New credential ID.""" + @dataclass + class To(ResourceMapping): + """New access schedule information. + + :ivar ends_at: Starting time for the access schedule. + + :ivar starts_at: Starting time for the access schedule.""" - email_address: Optional[str] - full_name: Optional[str] - phone_number: Optional[str] ends_at: Optional[str] starts_at: Optional[str] - is_suspended: Optional[bool] - acs_access_group_id: Optional[str] - acs_credential_id: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - email_address=d.get("email_address", None), - full_name=d.get("full_name", None), - phone_number=d.get("phone_number", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), - is_suspended=d.get("is_suspended", None), - acs_access_group_id=d.get("acs_access_group_id", None), - acs_credential_id=d.get("acs_credential_id", None), ) created_at: str + from_: Optional[From] message: str - mutation_code: str - scheduled_at: Optional[str] + mutation_code: Literal["updating_access_schedule"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingSuspensionStatePendingMutation(ResourceMapping): + """Seam is in the process of pushing a suspension state update to the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old user suspension state information. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated user suspension state information to the integrated access system. + + :ivar to: New user suspension state information.""" + + @dataclass + class From(ResourceMapping): + """Old user suspension state information. + + :ivar is_suspended:""" + + is_suspended: bool + + @classmethod + def from_dict(cls, d: Any): + return cls( + is_suspended=d.get("is_suspended", None), + ) + + @dataclass + class To(ResourceMapping): + """New user suspension state information. + + :ivar is_suspended:""" + + is_suspended: bool + + @classmethod + def from_dict(cls, d: Any): + return cls( + is_suspended=d.get("is_suspended", None), + ) + + created_at: str from_: Optional[From] + message: str + mutation_code: Literal["updating_suspension_state"] to: Optional[To] - acs_access_group_id: Optional[str] - variant: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), message=d.get("message", None), mutation_code=d.get("mutation_code", None), - scheduled_at=d.get("scheduled_at", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingGroupMembershipPendingMutation(ResourceMapping): + """Seam is in the process of pushing an access group membership update to the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Old access group membership. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of pushing updated access group membership information to the integrated access system. + + :ivar to: New access group membership.""" + + @dataclass + class From(ResourceMapping): + """Old access group membership. + + :ivar acs_access_group_id: Old access group ID.""" + + acs_access_group_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_access_group_id=d.get("acs_access_group_id", None), + ) + + @dataclass + class To(ResourceMapping): + """New access group membership. + + :ivar acs_access_group_id: New access group ID.""" + + acs_access_group_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_access_group_id=d.get("acs_access_group_id", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_group_membership"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), from_=( cls.From.from_dict(d.get("from")) if d.get("from") is not None else None ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class DeferringGroupMembershipUpdatePendingMutation(ResourceMapping): + """A scheduled access group membership change is pending for this user. + + :ivar acs_access_group_id: ID of the access group involved in the scheduled change. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that a scheduled access group membership change is pending for this user. + + :ivar variant: Whether the user is scheduled to be added to or removed from the access group. + """ + + acs_access_group_id: str + created_at: str + message: str + mutation_code: Literal["deferring_group_membership_update"] + variant: Literal["adding", "removing"] + + @classmethod + def from_dict(cls, d: Any): + return cls( acs_access_group_id=d.get("acs_access_group_id", None), + created_at=d.get("created_at", None), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), variant=d.get("variant", None), ) + @dataclass + class UpdatingCredentialAssignmentPendingMutation(ResourceMapping): + """Seam is in the process of assigning or unassigning a credential to the user on the integrated access system. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous credential assignment. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of assigning or unassigning a credential to the user on the integrated access system. + + :ivar to: New credential assignment.""" + + @dataclass + class From(ResourceMapping): + """Previous credential assignment. + + :ivar acs_credential_id: Previous credential ID.""" + + acs_credential_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_credential_id=d.get("acs_credential_id", None), + ) + + @dataclass + class To(ResourceMapping): + """New credential assignment. + + :ivar acs_credential_id: New credential ID.""" + + acs_credential_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_credential_id=d.get("acs_credential_id", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_credential_assignment"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + @dataclass class SaltoKsMetadata(ResourceMapping): """Salto KS-specific metadata associated with the `access system user `_. @@ -270,8 +698,8 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access system user `_. + class BeingDeletedWarning(ResourceMapping): + """Indicates that the `access system user `_ is being deleted from the `access system `_. This is a temporary state, and the access system user will be deleted shortly. :ivar created_at: Date and time at which Seam created the warning. @@ -281,7 +709,7 @@ class Warnings(ResourceMapping): created_at: str message: str - warning_code: str + warning_code: Literal["being_deleted"] @classmethod def from_dict(cls, d: Any): @@ -291,6 +719,149 @@ def from_dict(cls, d: Any): warning_code=d.get("warning_code", None), ) + @dataclass + class SaltoKsUserNotSubscribedWarning(ResourceMapping): + """Indicates that the `access system user `_ is not subscribed on Salto KS, so they cannot unlock doors or perform any actions. This occurs when the their access schedule hasn’t started yet, if their access schedule has ended, if the site has reached its limit for active users (subscription slots), or if they have been manually unsubscribed. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code:""" + + created_at: str + message: str + warning_code: Literal["salto_ks_user_not_subscribed"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class AcsUserInactiveWarning(ResourceMapping): + """Indicates that the `access system user `_ exists but is not currently able to gain access—for example, because their access schedule has not started yet or has ended, the access system has reached its limit for active users, or they have been unsubscribed or deactivated. Refer to the warning message for the provider-specific reason. This is distinct from ``is_suspended``, which indicates the user has been explicitly blocked. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code:""" + + created_at: str + message: str + warning_code: Literal["acs_user_inactive"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithAcsUserWarning(ResourceMapping): + """An unknown issue occurred while syncing the state of this `access system user `_ with the provider. This issue may affect the proper functioning of this user. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code:""" + + created_at: str + message: str + warning_code: Literal["unknown_issue_with_acs_user"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class LatchResidentUserWarning(ResourceMapping): + """Indicates that the `access system user `_ was created on Latch Mission Control. Please use the Latch Mission Control to manage this user. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code:""" + + created_at: str + message: str + warning_code: Literal["latch_resident_user"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + Errors = Union[ + DeletedExternallyError, + SaltoKsSubscriptionLimitExceededError, + FailedToCreateOnAcsSystemError, + FailedToUpdateOnAcsSystemError, + FailedToDeleteOnAcsSystemError, + LatchConflictWithResidentUserError, + ] + _ErrorsVariants = { + "deleted_externally": DeletedExternallyError, + "salto_ks_subscription_limit_exceeded": SaltoKsSubscriptionLimitExceededError, + "failed_to_create_on_acs_system": FailedToCreateOnAcsSystemError, + "failed_to_update_on_acs_system": FailedToUpdateOnAcsSystemError, + "failed_to_delete_on_acs_system": FailedToDeleteOnAcsSystemError, + "latch_conflict_with_resident_user": LatchConflictWithResidentUserError, + } + + PendingMutations = Union[ + CreatingPendingMutation, + DeletingPendingMutation, + DeferringCreationPendingMutation, + UpdatingUserInformationPendingMutation, + UpdatingAccessSchedulePendingMutation, + UpdatingSuspensionStatePendingMutation, + UpdatingGroupMembershipPendingMutation, + DeferringGroupMembershipUpdatePendingMutation, + UpdatingCredentialAssignmentPendingMutation, + ] + _PendingMutationsVariants = { + "creating": CreatingPendingMutation, + "deleting": DeletingPendingMutation, + "deferring_creation": DeferringCreationPendingMutation, + "updating_user_information": UpdatingUserInformationPendingMutation, + "updating_access_schedule": UpdatingAccessSchedulePendingMutation, + "updating_suspension_state": UpdatingSuspensionStatePendingMutation, + "updating_group_membership": UpdatingGroupMembershipPendingMutation, + "deferring_group_membership_update": DeferringGroupMembershipUpdatePendingMutation, + "updating_credential_assignment": UpdatingCredentialAssignmentPendingMutation, + } + + Warnings = Union[ + BeingDeletedWarning, + SaltoKsUserNotSubscribedWarning, + AcsUserInactiveWarning, + UnknownIssueWithAcsUserWarning, + LatchResidentUserWarning, + ] + _WarningsVariants = { + "being_deleted": BeingDeletedWarning, + "salto_ks_user_not_subscribed": SaltoKsUserNotSubscribedWarning, + "acs_user_inactive": AcsUserInactiveWarning, + "unknown_issue_with_acs_user": UnknownIssueWithAcsUserWarning, + "latch_resident_user": LatchResidentUserWarning, + } + access_schedule: Optional[AccessSchedule] acs_system_id: str acs_user_id: str @@ -300,7 +871,19 @@ def from_dict(cls, d: Any): email: Optional[str] email_address: Optional[str] errors: List[Errors] - external_type: Optional[str] + external_type: Optional[ + Literal[ + "pti_user", + "brivo_user", + "hid_credential_manager_user", + "salto_site_user", + "latch_user", + "dormakaba_community_user", + "salto_space_user", + "avigilon_alta_user", + "kisi_user", + ] + ] external_type_display_name: Optional[str] full_name: Optional[str] hid_acs_system_id: Optional[str] @@ -332,7 +915,10 @@ def from_dict(cls, d: Any): display_name=d.get("display_name", None), email=d.get("email", None), email_address=d.get("email_address", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], external_type=d.get("external_type", None), external_type_display_name=d.get("external_type_display_name", None), full_name=d.get("full_name", None), @@ -340,7 +926,9 @@ def from_dict(cls, d: Any): is_managed=d.get("is_managed", None), is_suspended=d.get("is_suspended", None), pending_mutations=[ - cls.PendingMutations.from_dict(i) + _from_discriminated_dict( + i, cls._PendingMutationsVariants, "mutation_code" + ) for i in d.get("pending_mutations") or [] ], phone_number=d.get("phone_number", None), @@ -358,6 +946,9 @@ def from_dict(cls, d: Any): user_identity_full_name=d.get("user_identity_full_name", None), user_identity_id=d.get("user_identity_id", None), user_identity_phone_number=d.get("user_identity_phone_number", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/action_attempt.py b/seam/resources/action_attempt.py index 852d5e02..bab15858 100644 --- a/seam/resources/action_attempt.py +++ b/seam/resources/action_attempt.py @@ -1,20 +1,20 @@ -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union, cast from dataclasses import dataclass from ..deep_attr_dict import DeepAttrDict from ..resource_mapping import ResourceMapping @dataclass -class ActionAttempt: - """An attempt to perform an action in the Seam API. +class LockDoorActionAttempt: + """Locking a door is pending. :ivar action_attempt_id: ID of the action attempt. - :ivar action_type: + :ivar action_type: Action attempt to track the status of locking a door. :ivar error: Error associated with the action. - :ivar result: + :ivar result: Result of the action. :ivar status:""" @@ -24,7 +24,7 @@ class Error(ResourceMapping): :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar type:""" + :ivar type: Type of the error.""" message: str type: str @@ -38,95 +38,166 @@ def from_dict(cls, d: Any): @dataclass class Result(ResourceMapping): - """ - - :ivar was_confirmed_by_device: - - :ivar acs_credential_on_encoder: Snapshot of credential data read from the physical encoder. - - :ivar acs_credential_on_seam: Corresponding credential data as stored on Seam and the access system. - - :ivar warnings: - - :ivar access_method: Access method for the `credential `_. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. + """Result of the action. - :ivar acs_credential_id: ID of the `credential `_. + :ivar was_confirmed_by_device: Indicates whether the device confirmed that the lock action occurred. + """ - :ivar acs_credential_pool_id: ID of the credential pool to which the credential belongs. + was_confirmed_by_device: Optional[bool] - :ivar acs_system_id: ID of the `access control system `_ that contains the `credential `_. + @classmethod + def from_dict(cls, d: Any): + return cls( + was_confirmed_by_device=d.get("was_confirmed_by_device", None), + ) - :ivar acs_user_id: ID of the `ACS user `_ to whom the `credential `_ belongs. + action_attempt_id: str + action_type: Literal["LOCK_DOOR"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "error"] - :ivar akiles_metadata: Akiles-specific metadata for the `credential `_. + @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 + ), + status=d.get("status", None), + ) - :ivar assa_abloy_vostio_metadata: Vostio-specific metadata for the `credential `_. - :ivar card_number: Number of the card associated with the `credential `_. +@dataclass +class UnlockDoorActionAttempt: + """Unlocking a door is pending. - :ivar code: + :ivar action_attempt_id: ID of the action attempt. - :ivar connected_account_id: ID of the `connected account `_ to which the `credential `_ belongs. + :ivar action_type: Action attempt to track the status of unlocking a door. - :ivar created_at: + :ivar error: Error associated with the action. - :ivar display_name: + :ivar result: Result of the action. - :ivar ends_at: Date and time at which the `credential `_ validity ends, in `ISO 8601 `_ format. Must be a time in the future and after ``starts_at``. + :ivar status:""" - :ivar errors: + @dataclass + class Error(ResourceMapping): + """Error associated with the action. - :ivar external_type: Brand-specific terminology for the `credential `_ type. Supported values: ``pti_card``, ``brivo_credential``, ``hid_credential``, ``visionline_card``. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar external_type_display_name: Display name that corresponds to the brand-specific terminology for the `credential `_ type. + :ivar type: Type of the error.""" - :ivar is_issued: + message: str + type: str - :ivar is_latest_desired_state_synced_with_provider: Indicates whether the latest state of the `credential `_ has been synced from Seam to the provider. + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) - :ivar is_managed: Indicates whether Seam manages the credential. + @dataclass + class Result(ResourceMapping): + """Result of the action. - :ivar is_multi_phone_sync_credential: Indicates whether the `credential `_ is a `multi-phone sync credential `_. + :ivar was_confirmed_by_device: Indicates whether the device confirmed that the unlock action occurred. + """ - :ivar is_one_time_use: Indicates whether the `credential `_ can only be used once. If ``true``, the code becomes invalid after the first use. + was_confirmed_by_device: Optional[bool] - :ivar issued_at: + @classmethod + def from_dict(cls, d: Any): + return cls( + was_confirmed_by_device=d.get("was_confirmed_by_device", None), + ) - :ivar latest_desired_state_synced_with_provider_at: Date and time at which the state of the `credential `_ was most recently synced from Seam to the provider. + action_attempt_id: str + action_type: Literal["UNLOCK_DOOR"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "error"] - :ivar parent_acs_credential_id: ID of the parent `credential `_. + @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 + ), + status=d.get("status", None), + ) - :ivar starts_at: Date and time at which the `credential `_ validity starts, in `ISO 8601 `_ format. - :ivar user_identity_id: ID of the `user identity `_ to whom the `credential `_ belongs. +@dataclass +class ScanCredentialActionAttempt: + """Reading credential data from the physical encoder is pending. - :ivar visionline_metadata: Visionline-specific metadata for the `credential `_. + :ivar action_attempt_id: ID of the action attempt. - :ivar workspace_id: + :ivar action_type: Action attempt to track the status of scanning a credential. - :ivar access_method_id: ID of the access method. + :ivar error: - :ivar client_session_token: Token of the client session associated with the access method. + :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 customization_profile_id: ID of the customization profile associated with the access method. + :ivar status:""" - :ivar instant_key_url: URL of the Instant Key for mobile key access methods. + @dataclass + class Error(ResourceMapping): + """ - :ivar is_assignment_required: Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - :ivar is_encoding_required: Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + :ivar type: Error type to indicate that the Seam Bridge is disconnected or cannot reach the access control system. + """ - :ivar is_ready_for_assignment: Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. + message: str + type: Literal[ + "uncategorized_error", + "action_attempt_expired", + "no_credential_on_encoder", + "encoder_not_online", + "encoder_communication_timeout", + "bridge_disconnected", + ] - :ivar is_ready_for_encoding: Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) - :ivar mode: Access method mode. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. + @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. - :ivar pending_mutations: Pending mutations for the `access method `_. Indicates operations that are in progress. + :ivar acs_credential_on_encoder: Snapshot of credential data read from the physical encoder. - :ivar access_code: + :ivar acs_credential_on_seam: Corresponding credential data as stored on Seam and the access system. - :ivar noise_threshold:""" + :ivar warnings: Warnings related to scanning the credential, such as mismatches between the credential data currently encoded on the card and the corresponding data stored on Seam and the access system. + """ @dataclass class AcsCredentialOnEncoder(ResourceMapping): @@ -175,7 +246,7 @@ class VisionlineMetadata(ResourceMapping): """ cancelled: Optional[bool] - card_format: Optional[str] + card_format: Optional[Literal["TLCode", "rfid48"]] card_holder: Optional[str] card_id: Optional[str] common_acs_entrance_ids: Optional[List[str]] @@ -384,7 +455,7 @@ class VisionlineMetadata(ResourceMapping): """ auto_join: Optional[bool] - card_function_type: Optional[str] + card_function_type: Optional[Literal["guest", "staff"]] card_id: Optional[str] common_acs_entrance_ids: Optional[List[str]] credential_id: Optional[str] @@ -424,7 +495,15 @@ class Warnings(ResourceMapping): created_at: str message: str - warning_code: str + warning_code: Literal[ + "waiting_to_be_issued", + "schedule_externally_modified", + "schedule_modified", + "being_deleted", + "unknown_issue_with_acs_credential", + "needs_to_be_reissued", + "requested_code_unavailable", + ] new_code: Optional[str] original_code: Optional[str] @@ -438,7 +517,7 @@ def from_dict(cls, d: Any): original_code=d.get("original_code", None), ) - access_method: str + access_method: Literal["code", "card", "mobile_key", "cloud_key"] acs_credential_id: str acs_credential_pool_id: Optional[str] acs_system_id: str @@ -452,7 +531,24 @@ def from_dict(cls, d: Any): display_name: str ends_at: Optional[str] errors: List[Errors] - external_type: Optional[str] + external_type: Optional[ + Literal[ + "pti_card", + "brivo_credential", + "hid_credential", + "visionline_card", + "salto_ks_credential", + "assa_abloy_vostio_key", + "salto_space_key", + "latch_access", + "dormakaba_ambiance_credential", + "hotek_card", + "salto_ks_tag", + "avigilon_alta_credential", + "kisi_credential", + "akiles_credential", + ] + ] external_type_display_name: Optional[str] is_issued: Optional[bool] is_latest_desired_state_synced_with_provider: Optional[bool] @@ -528,108 +624,244 @@ def from_dict(cls, d: Any): @dataclass class Warnings(ResourceMapping): - """ + """Warnings related to scanning the credential, such as mismatches between the credential data currently encoded on the card and the corresponding data stored on Seam and the access system. - :ivar warning_code: + :ivar warning_code: Indicates a warning related to scanning a credential. :ivar warning_message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - - :ivar created_at: Date and time at which Seam created the warning. - - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - - :ivar new_code: The PIN code that was assigned instead. - - :ivar original_code: The originally requested PIN code that could not be used. - - :ivar original_access_method_id: ID of the original access method from which this backup access method was split, if applicable. """ - warning_code: str - warning_message: Optional[str] - created_at: Optional[str] - message: Optional[str] - new_code: Optional[str] - original_code: Optional[str] - original_access_method_id: Optional[str] + warning_code: Literal[ + "acs_credential_on_encoder_out_of_sync", + "acs_credential_on_seam_not_found", + ] + warning_message: str @classmethod def from_dict(cls, d: Any): return cls( warning_code=d.get("warning_code", None), warning_message=d.get("warning_message", None), - created_at=d.get("created_at", None), - message=d.get("message", None), - new_code=d.get("new_code", None), - original_code=d.get("original_code", None), - original_access_method_id=d.get("original_access_method_id", None), ) - @dataclass - class AkilesMetadata(ResourceMapping): - """Akiles-specific metadata for the `credential `_. + acs_credential_on_encoder: Optional[AcsCredentialOnEncoder] + acs_credential_on_seam: Optional[AcsCredentialOnSeam] + warnings: List[Warnings] - :ivar member_pin_id: ID of the Akiles member PIN.""" + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_credential_on_encoder=( + cls.AcsCredentialOnEncoder.from_dict( + d.get("acs_credential_on_encoder") + ) + if d.get("acs_credential_on_encoder") is not None + else None + ), + acs_credential_on_seam=( + cls.AcsCredentialOnSeam.from_dict(d.get("acs_credential_on_seam")) + if d.get("acs_credential_on_seam") is not None + else None + ), + warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + ) - member_pin_id: Optional[str] + action_attempt_id: str + action_type: Literal["SCAN_CREDENTIAL"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "error"] - @classmethod - def from_dict(cls, d: Any): - return cls( - member_pin_id=d.get("member_pin_id", None), - ) + @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 + ), + status=d.get("status", None), + ) - @dataclass - class AssaAbloyVostioMetadata(ResourceMapping): - """Vostio-specific metadata for the `credential `_. - :ivar auto_join: Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. +@dataclass +class EncodeCredentialActionAttempt: + """Encoding credential data from the physical encoder onto a card is pending. - :ivar door_names: Names of the doors to which to grant access in the Vostio access system. + :ivar action_attempt_id: ID of the action attempt. - :ivar endpoint_id: Endpoint ID in the Vostio access system. + :ivar action_type: Action attempt to track the status of encoding credential data from the physical encoder onto a card. - :ivar key_id: Key ID in the Vostio access system. + :ivar error: - :ivar key_issuing_request_id: Key issuing request ID in the Vostio access system. + :ivar result: Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. - :ivar override_guest_acs_entrance_ids: IDs of the guest entrances to override in the Vostio access system. - """ + :ivar status:""" - auto_join: Optional[bool] - door_names: Optional[List[str]] - endpoint_id: Optional[str] - key_id: Optional[str] - key_issuing_request_id: Optional[str] - override_guest_acs_entrance_ids: Optional[List[str]] + @dataclass + class Error(ResourceMapping): + """ - @classmethod - def from_dict(cls, d: Any): - return cls( - auto_join=d.get("auto_join", None), - door_names=d.get("door_names", None), - endpoint_id=d.get("endpoint_id", None), - key_id=d.get("key_id", None), - key_issuing_request_id=d.get("key_issuing_request_id", None), - override_guest_acs_entrance_ids=d.get( - "override_guest_acs_entrance_ids", None - ), - ) + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - @dataclass - class Errors(ResourceMapping): - """ + :ivar type: Error type to indicate that the credential was deleted and can no longer be encoded. + """ - :ivar created_at: Date and time at which Seam created the error. + message: str + type: Literal[ + "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", + ] - :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + type=d.get("type", None), + ) - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - """ + @dataclass + class Result(ResourceMapping): + """Result of an encoding attempt. If the attempt was successful, includes the credential data that was encoded onto the card. - created_at: str - error_code: str - message: str + :ivar access_method: Access method for the `credential `_. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. + + :ivar acs_credential_id: ID of the `credential `_. + + :ivar acs_credential_pool_id: ID of the credential pool to which the credential belongs. + + :ivar acs_system_id: ID of the `access control system `_ that contains the `credential `_. + + :ivar acs_user_id: ID of the `ACS user `_ to whom the `credential `_ belongs. + + :ivar akiles_metadata: Akiles-specific metadata for the `credential `_. + + :ivar assa_abloy_vostio_metadata: Vostio-specific metadata for the `credential `_. + + :ivar card_number: Number of the card associated with the `credential `_. + + :ivar code: Access (PIN) code for the `credential `_. + + :ivar connected_account_id: ID of the `connected account `_ to which the `credential `_ belongs. + + :ivar created_at: Date and time at which the `credential `_ was created. + + :ivar display_name: Display name that corresponds to the `credential `_ type. + + :ivar ends_at: Date and time at which the `credential `_ validity ends, in `ISO 8601 `_ format. Must be a time in the future and after ``starts_at``. + + :ivar errors: Errors associated with the `credential `_. + + :ivar external_type: Brand-specific terminology for the `credential `_ type. Supported values: ``pti_card``, ``brivo_credential``, ``hid_credential``, ``visionline_card``. + + :ivar external_type_display_name: Display name that corresponds to the brand-specific terminology for the `credential `_ type. + + :ivar is_issued: Indicates whether the `credential `_ has been encoded onto a card. + + :ivar is_latest_desired_state_synced_with_provider: Indicates whether the latest state of the `credential `_ has been synced from Seam to the provider. + + :ivar is_managed: + + :ivar is_multi_phone_sync_credential: Indicates whether the `credential `_ is a `multi-phone sync credential `_. + + :ivar is_one_time_use: Indicates whether the `credential `_ can only be used once. If ``true``, the code becomes invalid after the first use. + + :ivar issued_at: Date and time at which the `credential `_ was encoded onto a card. + + :ivar latest_desired_state_synced_with_provider_at: Date and time at which the state of the `credential `_ was most recently synced from Seam to the provider. + + :ivar parent_acs_credential_id: ID of the parent `credential `_. + + :ivar starts_at: Date and time at which the `credential `_ validity starts, in `ISO 8601 `_ format. + + :ivar user_identity_id: ID of the `user identity `_ to whom the `credential `_ belongs. + + :ivar visionline_metadata: Visionline-specific metadata for the `credential `_. + + :ivar warnings: Warnings associated with the `credential `_. + + :ivar workspace_id: ID of the workspace that contains the `credential `_. + """ + + @dataclass + class AkilesMetadata(ResourceMapping): + """Akiles-specific metadata for the `credential `_. + + :ivar member_pin_id: ID of the Akiles member PIN.""" + + member_pin_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + member_pin_id=d.get("member_pin_id", None), + ) + + @dataclass + class AssaAbloyVostioMetadata(ResourceMapping): + """Vostio-specific metadata for the `credential `_. + + :ivar auto_join: Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + + :ivar door_names: Names of the doors to which to grant access in the Vostio access system. + + :ivar endpoint_id: Endpoint ID in the Vostio access system. + + :ivar key_id: Key ID in the Vostio access system. + + :ivar key_issuing_request_id: Key issuing request ID in the Vostio access system. + + :ivar override_guest_acs_entrance_ids: IDs of the guest entrances to override in the Vostio access system. + """ + + auto_join: Optional[bool] + door_names: Optional[List[str]] + endpoint_id: Optional[str] + key_id: Optional[str] + key_issuing_request_id: Optional[str] + override_guest_acs_entrance_ids: Optional[List[str]] + + @classmethod + def from_dict(cls, d: Any): + return cls( + auto_join=d.get("auto_join", None), + door_names=d.get("door_names", None), + endpoint_id=d.get("endpoint_id", None), + key_id=d.get("key_id", None), + key_issuing_request_id=d.get("key_issuing_request_id", None), + override_guest_acs_entrance_ids=d.get( + "override_guest_acs_entrance_ids", None + ), + ) + + @dataclass + class Errors(ResourceMapping): + """Errors associated with the `credential `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: + + :ivar message:""" + + created_at: str + error_code: str + message: str @classmethod def from_dict(cls, d: Any): @@ -661,7 +893,7 @@ class VisionlineMetadata(ResourceMapping): """ auto_join: Optional[bool] - card_function_type: Optional[str] + card_function_type: Optional[Literal["guest", "staff"]] card_id: Optional[str] common_acs_entrance_ids: Optional[List[str]] credential_id: Optional[str] @@ -683,102 +915,80 @@ def from_dict(cls, d: Any): ) @dataclass - class PendingMutations(ResourceMapping): - """Pending mutations for the `access method `_. Indicates operations that are in progress. - - :ivar created_at: Date and time at which the mutation was created. - - :ivar from_: Previous access time configuration. - - :ivar message: Detailed description of the mutation. - - :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the access times for this access method. - - :ivar to: New access time configuration.""" - - @dataclass - class From(ResourceMapping): - """Previous access time configuration. - - :ivar ends_at: Previous end time for access. - - :ivar starts_at: Previous start time for access.""" - - ends_at: Optional[str] - starts_at: Optional[str] - - @classmethod - def from_dict(cls, d: Any): - return cls( - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), - ) + class Warnings(ResourceMapping): + """Warnings associated with the `credential `_. - @dataclass - class To(ResourceMapping): - """New access time configuration. + :ivar created_at: Date and time at which Seam created the warning. - :ivar ends_at: New end time for access. + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - :ivar starts_at: New start time for access.""" + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - ends_at: Optional[str] - starts_at: Optional[str] + :ivar new_code: The PIN code that was assigned instead. - @classmethod - def from_dict(cls, d: Any): - return cls( - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), - ) + :ivar original_code: The originally requested PIN code that could not be used. + """ created_at: str - from_: Optional[From] message: str - mutation_code: str - to: Optional[To] + warning_code: Literal[ + "waiting_to_be_issued", + "schedule_externally_modified", + "schedule_modified", + "being_deleted", + "unknown_issue_with_acs_credential", + "needs_to_be_reissued", + "requested_code_unavailable", + ] + new_code: Optional[str] + original_code: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), - from_=( - cls.From.from_dict(d.get("from")) - if d.get("from") is not None - else None - ), message=d.get("message", None), - mutation_code=d.get("mutation_code", None), - to=( - cls.To.from_dict(d.get("to")) - if d.get("to") is not None - else None - ), + warning_code=d.get("warning_code", None), + new_code=d.get("new_code", None), + original_code=d.get("original_code", None), ) - was_confirmed_by_device: Optional[bool] - acs_credential_on_encoder: Optional[AcsCredentialOnEncoder] - acs_credential_on_seam: Optional[AcsCredentialOnSeam] - warnings: Optional[List[Warnings]] - access_method: Optional[str] - acs_credential_id: Optional[str] + access_method: Literal["code", "card", "mobile_key", "cloud_key"] + acs_credential_id: str acs_credential_pool_id: Optional[str] - acs_system_id: Optional[str] + acs_system_id: str acs_user_id: Optional[str] akiles_metadata: Optional[AkilesMetadata] assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] card_number: Optional[str] code: Optional[str] - connected_account_id: Optional[str] - created_at: Optional[str] - display_name: Optional[str] + connected_account_id: str + created_at: str + display_name: str ends_at: Optional[str] - errors: Optional[List[Errors]] - external_type: Optional[str] + errors: List[Errors] + external_type: Optional[ + Literal[ + "pti_card", + "brivo_credential", + "hid_credential", + "visionline_card", + "salto_ks_credential", + "assa_abloy_vostio_key", + "salto_space_key", + "latch_access", + "dormakaba_ambiance_credential", + "hotek_card", + "salto_ks_tag", + "avigilon_alta_credential", + "kisi_credential", + "akiles_credential", + ] + ] external_type_display_name: Optional[str] is_issued: Optional[bool] is_latest_desired_state_synced_with_provider: Optional[bool] - is_managed: Optional[Literal[True, False]] + is_managed: Literal[True, False] is_multi_phone_sync_credential: Optional[bool] is_one_time_use: Optional[bool] issued_at: Optional[str] @@ -787,37 +997,12 @@ def from_dict(cls, d: Any): starts_at: Optional[str] user_identity_id: Optional[str] visionline_metadata: Optional[VisionlineMetadata] - workspace_id: Optional[str] - access_method_id: Optional[str] - client_session_token: Optional[str] - customization_profile_id: Optional[str] - instant_key_url: Optional[str] - is_assignment_required: Optional[bool] - is_encoding_required: Optional[bool] - is_ready_for_assignment: Optional[bool] - is_ready_for_encoding: Optional[bool] - mode: Optional[str] - pending_mutations: Optional[List[PendingMutations]] - access_code: Optional[Dict[str, Any]] - noise_threshold: Optional[Dict[str, Any]] + warnings: List[Warnings] + workspace_id: str @classmethod def from_dict(cls, d: Any): return cls( - was_confirmed_by_device=d.get("was_confirmed_by_device", None), - acs_credential_on_encoder=( - cls.AcsCredentialOnEncoder.from_dict( - d.get("acs_credential_on_encoder") - ) - if d.get("acs_credential_on_encoder") is not None - else None - ), - acs_credential_on_seam=( - cls.AcsCredentialOnSeam.from_dict(d.get("acs_credential_on_seam")) - if d.get("acs_credential_on_seam") is not None - else None - ), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], access_method=d.get("access_method", None), acs_credential_id=d.get("acs_credential_id", None), acs_credential_pool_id=d.get("acs_credential_pool_id", None), @@ -865,29 +1050,15 @@ def from_dict(cls, d: Any): if d.get("visionline_metadata") is not None else None ), + warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], workspace_id=d.get("workspace_id", None), - access_method_id=d.get("access_method_id", None), - client_session_token=d.get("client_session_token", None), - customization_profile_id=d.get("customization_profile_id", None), - instant_key_url=d.get("instant_key_url", None), - is_assignment_required=d.get("is_assignment_required", None), - is_encoding_required=d.get("is_encoding_required", None), - is_ready_for_assignment=d.get("is_ready_for_assignment", None), - is_ready_for_encoding=d.get("is_ready_for_encoding", None), - mode=d.get("mode", None), - pending_mutations=[ - cls.PendingMutations.from_dict(i) - for i in d.get("pending_mutations") or [] - ], - access_code=DeepAttrDict(d.get("access_code", None)), - noise_threshold=DeepAttrDict(d.get("noise_threshold", None)), ) action_attempt_id: str - action_type: str + action_type: Literal["ENCODE_CREDENTIAL"] error: Optional[Error] result: Optional[Result] - status: str + status: Literal["success", "pending", "error"] @classmethod def from_dict(cls, d: Any): @@ -906,3 +1077,1729 @@ def from_dict(cls, d: Any): ), status=d.get("status", None), ) + + +@dataclass +class ScanToAssignCredentialActionAttempt: + """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 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. + """ + + message: str + type: Literal[ + "uncategorized_error", "action_attempt_expired", "no_credential_on_encoder" + ] + + @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 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 `_. + + :ivar acs_credential_pool_id: ID of the credential pool to which the credential belongs. + + :ivar acs_system_id: ID of the `access control system `_ that contains the `credential `_. + + :ivar acs_user_id: ID of the `ACS user `_ to whom the `credential `_ belongs. + + :ivar akiles_metadata: Akiles-specific metadata for the `credential `_. + + :ivar assa_abloy_vostio_metadata: Vostio-specific metadata for the `credential `_. + + :ivar card_number: Number of the card associated with the `credential `_. + + :ivar code: Access (PIN) code for the `credential `_. + + :ivar connected_account_id: ID of the `connected account `_ to which the `credential `_ belongs. + + :ivar created_at: Date and time at which the `credential `_ was created. + + :ivar display_name: Display name that corresponds to the `credential `_ type. + + :ivar ends_at: Date and time at which the `credential `_ validity ends, in `ISO 8601 `_ format. Must be a time in the future and after ``starts_at``. + + :ivar errors: Errors associated with the `credential `_. + + :ivar external_type: Brand-specific terminology for the `credential `_ type. Supported values: ``pti_card``, ``brivo_credential``, ``hid_credential``, ``visionline_card``. + + :ivar external_type_display_name: Display name that corresponds to the brand-specific terminology for the `credential `_ type. + + :ivar is_issued: Indicates whether the `credential `_ has been encoded onto a card. + + :ivar is_latest_desired_state_synced_with_provider: Indicates whether the latest state of the `credential `_ has been synced from Seam to the provider. + + :ivar is_managed: Indicates whether Seam manages the credential. + + :ivar is_multi_phone_sync_credential: Indicates whether the `credential `_ is a `multi-phone sync credential `_. + + :ivar is_one_time_use: Indicates whether the `credential `_ can only be used once. If ``true``, the code becomes invalid after the first use. + + :ivar issued_at: Date and time at which the `credential `_ was encoded onto a card. + + :ivar latest_desired_state_synced_with_provider_at: Date and time at which the state of the `credential `_ was most recently synced from Seam to the provider. + + :ivar parent_acs_credential_id: ID of the parent `credential `_. + + :ivar starts_at: Date and time at which the `credential `_ validity starts, in `ISO 8601 `_ format. + + :ivar user_identity_id: ID of the `user identity `_ to whom the `credential `_ belongs. + + :ivar visionline_metadata: Visionline-specific metadata for the `credential `_. + + :ivar warnings: Warnings associated with the `credential `_. + + :ivar workspace_id: ID of the workspace that contains the `credential `_. + """ + + @dataclass + class AkilesMetadata(ResourceMapping): + """Akiles-specific metadata for the `credential `_. + + :ivar member_pin_id: ID of the Akiles member PIN.""" + + member_pin_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + member_pin_id=d.get("member_pin_id", None), + ) + + @dataclass + class AssaAbloyVostioMetadata(ResourceMapping): + """Vostio-specific metadata for the `credential `_. + + :ivar auto_join: Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + + :ivar door_names: Names of the doors to which to grant access in the Vostio access system. + + :ivar endpoint_id: Endpoint ID in the Vostio access system. + + :ivar key_id: Key ID in the Vostio access system. + + :ivar key_issuing_request_id: Key issuing request ID in the Vostio access system. + + :ivar override_guest_acs_entrance_ids: IDs of the guest entrances to override in the Vostio access system. + """ + + auto_join: Optional[bool] + door_names: Optional[List[str]] + endpoint_id: Optional[str] + key_id: Optional[str] + key_issuing_request_id: Optional[str] + override_guest_acs_entrance_ids: Optional[List[str]] + + @classmethod + def from_dict(cls, d: Any): + return cls( + auto_join=d.get("auto_join", None), + door_names=d.get("door_names", None), + endpoint_id=d.get("endpoint_id", None), + key_id=d.get("key_id", None), + key_issuing_request_id=d.get("key_issuing_request_id", None), + override_guest_acs_entrance_ids=d.get( + "override_guest_acs_entrance_ids", None + ), + ) + + @dataclass + class Errors(ResourceMapping): + """Errors associated with the `credential `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: + + :ivar message:""" + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class VisionlineMetadata(ResourceMapping): + """Visionline-specific metadata for the `credential `_. + + :ivar auto_join: Indicates whether the credential should auto-join. For an auto-join credential, Seam automatically issues an override card if there are no other cards and a joiner card if there are existing cards on the doors. + + :ivar card_function_type: Card function type in the Visionline access system. + + :ivar card_id: ID of the card in the Visionline access system. + + :ivar common_acs_entrance_ids: Common entrance IDs in the Visionline access system. + + :ivar credential_id: ID of the credential in the Visionline access system. + + :ivar guest_acs_entrance_ids: Guest entrance IDs in the Visionline access system. + + :ivar is_valid: Indicates whether the credential is valid. + + :ivar joiner_acs_credential_ids: IDs of the credentials to which you want to join. + """ + + auto_join: Optional[bool] + card_function_type: Optional[Literal["guest", "staff"]] + card_id: Optional[str] + common_acs_entrance_ids: Optional[List[str]] + credential_id: Optional[str] + guest_acs_entrance_ids: Optional[List[str]] + is_valid: Optional[bool] + joiner_acs_credential_ids: Optional[List[str]] + + @classmethod + def from_dict(cls, d: Any): + return cls( + auto_join=d.get("auto_join", None), + card_function_type=d.get("card_function_type", None), + card_id=d.get("card_id", None), + common_acs_entrance_ids=d.get("common_acs_entrance_ids", None), + credential_id=d.get("credential_id", None), + guest_acs_entrance_ids=d.get("guest_acs_entrance_ids", None), + is_valid=d.get("is_valid", None), + joiner_acs_credential_ids=d.get("joiner_acs_credential_ids", None), + ) + + @dataclass + class Warnings(ResourceMapping): + """Warnings associated with the `credential `_. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + + :ivar new_code: The PIN code that was assigned instead. + + :ivar original_code: The originally requested PIN code that could not be used. + """ + + created_at: str + message: str + warning_code: Literal[ + "waiting_to_be_issued", + "schedule_externally_modified", + "schedule_modified", + "being_deleted", + "unknown_issue_with_acs_credential", + "needs_to_be_reissued", + "requested_code_unavailable", + ] + new_code: Optional[str] + original_code: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + new_code=d.get("new_code", None), + original_code=d.get("original_code", None), + ) + + access_method: Literal["code", "card", "mobile_key", "cloud_key"] + acs_credential_id: str + acs_credential_pool_id: Optional[str] + acs_system_id: str + acs_user_id: Optional[str] + akiles_metadata: Optional[AkilesMetadata] + assa_abloy_vostio_metadata: Optional[AssaAbloyVostioMetadata] + card_number: Optional[str] + code: Optional[str] + connected_account_id: str + created_at: str + display_name: str + ends_at: Optional[str] + errors: List[Errors] + external_type: Optional[ + Literal[ + "pti_card", + "brivo_credential", + "hid_credential", + "visionline_card", + "salto_ks_credential", + "assa_abloy_vostio_key", + "salto_space_key", + "latch_access", + "dormakaba_ambiance_credential", + "hotek_card", + "salto_ks_tag", + "avigilon_alta_credential", + "kisi_credential", + "akiles_credential", + ] + ] + external_type_display_name: Optional[str] + is_issued: Optional[bool] + is_latest_desired_state_synced_with_provider: Optional[bool] + is_managed: Literal[True] + is_multi_phone_sync_credential: Optional[bool] + is_one_time_use: Optional[bool] + issued_at: Optional[str] + latest_desired_state_synced_with_provider_at: Optional[str] + parent_acs_credential_id: Optional[str] + starts_at: Optional[str] + user_identity_id: Optional[str] + visionline_metadata: Optional[VisionlineMetadata] + warnings: List[Warnings] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_method=d.get("access_method", None), + acs_credential_id=d.get("acs_credential_id", None), + acs_credential_pool_id=d.get("acs_credential_pool_id", None), + acs_system_id=d.get("acs_system_id", None), + acs_user_id=d.get("acs_user_id", None), + akiles_metadata=( + cls.AkilesMetadata.from_dict(d.get("akiles_metadata")) + if d.get("akiles_metadata") is not None + else None + ), + assa_abloy_vostio_metadata=( + cls.AssaAbloyVostioMetadata.from_dict( + d.get("assa_abloy_vostio_metadata") + ) + if d.get("assa_abloy_vostio_metadata") is not None + else None + ), + card_number=d.get("card_number", None), + code=d.get("code", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + display_name=d.get("display_name", None), + ends_at=d.get("ends_at", None), + errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + external_type=d.get("external_type", None), + external_type_display_name=d.get("external_type_display_name", None), + is_issued=d.get("is_issued", None), + is_latest_desired_state_synced_with_provider=d.get( + "is_latest_desired_state_synced_with_provider", None + ), + is_managed=d.get("is_managed", None), + is_multi_phone_sync_credential=d.get( + "is_multi_phone_sync_credential", None + ), + is_one_time_use=d.get("is_one_time_use", None), + issued_at=d.get("issued_at", None), + latest_desired_state_synced_with_provider_at=d.get( + "latest_desired_state_synced_with_provider_at", None + ), + parent_acs_credential_id=d.get("parent_acs_credential_id", None), + starts_at=d.get("starts_at", None), + user_identity_id=d.get("user_identity_id", None), + visionline_metadata=( + cls.VisionlineMetadata.from_dict(d.get("visionline_metadata")) + if d.get("visionline_metadata") is not None + else None + ), + warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + workspace_id=d.get("workspace_id", None), + ) + + action_attempt_id: str + action_type: Literal["SCAN_TO_ASSIGN_CREDENTIAL"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class AssignCredentialActionAttempt: + """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 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.""" + + message: str + type: Literal[ + "uncategorized_error", "action_attempt_expired", "credential_not_found" + ] + + @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 assigning a credential. If successful, includes the updated access method with the assigned credential. + + :ivar access_method_id: ID of the access method. + + :ivar client_session_token: Token of the client session associated with the access method. + + :ivar code: The actual PIN code for code access methods. + + :ivar created_at: Date and time at which the access method was created. + + :ivar customization_profile_id: ID of the customization profile associated with the access method. + + :ivar display_name: Display name of the access method. + + :ivar errors: Errors associated with the `access method `_. + + :ivar instant_key_url: URL of the Instant Key for mobile key access methods. + + :ivar is_assignment_required: Indicates whether an existing card credential must be assigned to this access method before it can be issued. Only applies to card-mode access methods on systems that support credential assignment. + + :ivar is_encoding_required: Indicates whether encoding with an card encoder is required to issue or reissue the plastic card associated with the access method. + + :ivar is_issued: Indicates whether the access method has been issued. + + :ivar is_ready_for_assignment: Indicates whether the access method is ready for card assignment. This is true when the access method is in card mode, has not yet been issued, and the system supports credential assignment. + + :ivar is_ready_for_encoding: Indicates whether the access method is ready to be encoded. This is true when the credential has been created and the card has not yet been issued. + + :ivar issued_at: Date and time at which the access method was issued. + + :ivar mode: Access method mode. Supported values: ``code``, ``card``, ``mobile_key``, ``cloud_key``. + + :ivar pending_mutations: Pending mutations for the `access method `_. Indicates operations that are in progress. + + :ivar warnings: Warnings associated with the `access method `_. + + :ivar workspace_id: ID of the Seam workspace associated with the access method. + """ + + @dataclass + class Errors(ResourceMapping): + """Errors associated with the `access method `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["failed_to_issue"] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class PendingMutations(ResourceMapping): + """Pending mutations for the `access method `_. Indicates operations that are in progress. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous access time configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the access times for this access method. + + :ivar to: New access time configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous access time configuration. + + :ivar ends_at: Previous end time for access. + + :ivar starts_at: Previous start time for access.""" + + ends_at: Optional[str] + starts_at: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + ends_at=d.get("ends_at", None), + starts_at=d.get("starts_at", None), + ) + + @dataclass + class To(ResourceMapping): + """New access time configuration. + + :ivar ends_at: New end time for access. + + :ivar starts_at: New start time for access.""" + + ends_at: Optional[str] + starts_at: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + ends_at=d.get("ends_at", None), + starts_at=d.get("starts_at", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal[ + "provisioning_access", "revoking_access", "updating_access_times" + ] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=( + cls.To.from_dict(d.get("to")) + if d.get("to") is not None + else None + ), + ) + + @dataclass + class Warnings(ResourceMapping): + """Warnings associated with the `access method `_. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + + :ivar original_access_method_id: ID of the original access method from which this backup access method was split, if applicable. + """ + + created_at: str + message: str + warning_code: Literal[ + "being_deleted", + "updating_access_times", + "pulled_backup_access_code", + "delay_in_issuing", + ] + original_access_method_id: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + original_access_method_id=d.get("original_access_method_id", None), + ) + + access_method_id: str + client_session_token: Optional[str] + code: Optional[str] + created_at: str + customization_profile_id: Optional[str] + display_name: str + errors: List[Errors] + instant_key_url: Optional[str] + is_assignment_required: Optional[bool] + is_encoding_required: Optional[bool] + is_issued: bool + is_ready_for_assignment: Optional[bool] + is_ready_for_encoding: Optional[bool] + issued_at: Optional[str] + mode: Literal["code", "card", "mobile_key", "cloud_key"] + pending_mutations: List[PendingMutations] + warnings: List[Warnings] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_method_id=d.get("access_method_id", None), + client_session_token=d.get("client_session_token", None), + code=d.get("code", None), + created_at=d.get("created_at", None), + customization_profile_id=d.get("customization_profile_id", None), + display_name=d.get("display_name", None), + errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + instant_key_url=d.get("instant_key_url", None), + is_assignment_required=d.get("is_assignment_required", None), + is_encoding_required=d.get("is_encoding_required", None), + is_issued=d.get("is_issued", None), + is_ready_for_assignment=d.get("is_ready_for_assignment", None), + is_ready_for_encoding=d.get("is_ready_for_encoding", None), + issued_at=d.get("issued_at", None), + mode=d.get("mode", None), + pending_mutations=[ + cls.PendingMutations.from_dict(i) + for i in d.get("pending_mutations") or [] + ], + warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + workspace_id=d.get("workspace_id", None), + ) + + action_attempt_id: str + action_type: Literal["ASSIGN_CREDENTIAL"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class ResetSandboxWorkspaceActionAttempt: + """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 resetting a sandbox workspace. + + :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), + ) + + @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"] + + @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 + ), + status=d.get("status", None), + ) + + +@dataclass +class SetFanModeActionAttempt: + """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.""" + + 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["SET_FAN_MODE"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class SetHvacModeActionAttempt: + """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), + ) + + @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_HVAC_MODE"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class ActivateClimatePresetActionAttempt: + """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 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"] + + @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 + ), + status=d.get("status", None), + ) + + +@dataclass +class SimulateKeypadCodeEntryActionAttempt: + """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), + ) + + @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"] + + @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 + ), + status=d.get("status", None), + ) + + +@dataclass +class SimulateManualLockViaKeypadActionAttempt: + """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), + ) + + @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: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class PushThermostatProgramsActionAttempt: + """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), + ) + + @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: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class ConfigureAutoLockActionAttempt: + """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), + ) + + @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: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class SyncAccessCodesActionAttempt: + """ + + :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), + ) + + @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"] + + @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 + ), + status=d.get("status", None), + ) + + +@dataclass +class CreateAccessCodeActionAttempt: + """ + + :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), + ) + + @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: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class DeleteAccessCodeActionAttempt: + """ + + :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), + ) + + @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"] + + @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 + ), + status=d.get("status", None), + ) + + +@dataclass +class UpdateAccessCodeActionAttempt: + """ + + :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 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. + + :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: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class CreateNoiseThresholdActionAttempt: + """ + + :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:""" + + @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. + + :ivar noise_threshold: Created 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["CREATE_NOISE_THRESHOLD"] + error: Optional[Error] + result: Optional[Result] + status: Literal["success", "pending", "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 + ), + status=d.get("status", None), + ) + + +@dataclass +class DeleteNoiseThresholdActionAttempt: + """ + + :ivar action_attempt_id: ID of the action attempt. + + :ivar action_type: Deleting a noise threshold 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), + ) + + @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"] + + @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 + ), + status=d.get("status", None), + ) + + +@dataclass +class UpdateNoiseThresholdActionAttempt: + """ + + :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 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. + + :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"] + + @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 + ), + 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, +] + +_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, +} + + +def action_attempt_from_dict(d: Any) -> ActionAttempt: + """Deserialize a known action_type 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")) + if variant is None: + return cast(ActionAttempt, DeepAttrDict(d)) + return variant.from_dict(d) diff --git a/seam/resources/connect_webview.py b/seam/resources/connect_webview.py index 15f8f28d..3484381b 100644 --- a/seam/resources/connect_webview.py +++ b/seam/resources/connect_webview.py @@ -56,7 +56,9 @@ class ConnectWebview: :ivar workspace_id: ID of the workspace that contains the Connect Webview.""" - accepted_capabilities: List[str] + accepted_capabilities: List[ + Literal["lock", "thermostat", "noise_sensor", "access_control", "camera"] + ] accepted_providers: List[str] any_provider_allowed: bool authorized_at: Optional[str] @@ -68,10 +70,10 @@ class ConnectWebview: custom_redirect_failure_url: Optional[str] custom_redirect_url: Optional[str] customer_key: Optional[str] - device_selection_mode: str + device_selection_mode: Literal["none", "single", "multiple"] login_successful: bool selected_provider: Optional[str] - status: str + status: Literal["pending", "failed", "authorized"] url: str wait_for_device_creation: bool workspace_id: str diff --git a/seam/resources/connected_account.py b/seam/resources/connected_account.py index fc7d9c59..64918531 100644 --- a/seam/resources/connected_account.py +++ b/seam/resources/connected_account.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class ConnectedAccount: """Represents a `connected account `_. A connected account is an external third-party account to which your user has authorized Seam to get access, for example, an August account with a list of door locks. @@ -45,8 +52,70 @@ class ConnectedAccount: :ivar warnings: Warnings associated with the connected account.""" @dataclass - class Errors(ResourceMapping): - """Errors associated with the connected account. + class AccountDisconnectedError(ResourceMapping): + """Indicates that the account is disconnected. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + + :ivar is_connected_account_error: Indicates whether the error is related specifically to the connected account. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["account_disconnected"] + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_bridge_error=d.get("is_bridge_error", None), + is_connected_account_error=d.get("is_connected_account_error", None), + message=d.get("message", None), + ) + + @dataclass + class BridgeDisconnectedError(ResourceMapping): + """Indicates that the Seam API cannot communicate with `Seam Bridge `_, for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also `Troubleshooting Your Access Control System `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + + :ivar is_connected_account_error: Indicates whether the error is related specifically to the connected account. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["bridge_disconnected"] + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_bridge_error=d.get("is_bridge_error", None), + is_connected_account_error=d.get("is_connected_account_error", None), + message=d.get("message", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitExceededError(ResourceMapping): + """Indicates that the maximum number of users allowed for the site has been reached. This means that new access codes cannot be created. Contact Salto support to increase the user limit. :ivar created_at: Date and time at which Seam created the error. @@ -108,7 +177,7 @@ def from_dict(cls, d: Any): ) created_at: str - error_code: str + error_code: Literal["salto_ks_subscription_limit_exceeded"] is_bridge_error: Optional[bool] is_connected_account_error: Optional[bool] message: str @@ -129,6 +198,37 @@ def from_dict(cls, d: Any): ), ) + @dataclass + class DormakabaSitesDisconnectedError(ResourceMapping): + """Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + + :ivar is_connected_account_error: Indicates whether the error is related specifically to the connected account. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["dormakaba_sites_disconnected"] + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_bridge_error=d.get("is_bridge_error", None), + is_connected_account_error=d.get("is_connected_account_error", None), + message=d.get("message", None), + ) + @dataclass class UserIdentifier(ResourceMapping): """User identifier associated with the connected account. @@ -161,16 +261,62 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the connected account. + class ScheduledMaintenanceWindowWarning(ResourceMapping): + """Indicates that scheduled downtime is planned for the connected account. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["scheduled_maintenance_window"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithConnectedAccountWarning(ResourceMapping): + """Indicates that an unknown issue occurred while syncing the state of the connected account with the provider. This issue may affect the proper functioning of one or more resources in the account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["unknown_issue_with_connected_account"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitAlmostReachedWarning(ResourceMapping): + """Indicates that the Salto KS site has exceeded 80% of the maximum number of allowed users. Increase your subscription limit or delete some users from your site. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar salto_ks_metadata: Salto KS metadata associated with the connected account that has a warning. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. """ @dataclass @@ -221,23 +367,174 @@ def from_dict(cls, d: Any): created_at: str message: str - warning_code: str salto_ks_metadata: Optional[SaltoKsMetadata] + warning_code: Literal["salto_ks_subscription_limit_almost_reached"] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), salto_ks_metadata=( cls.SaltoKsMetadata.from_dict(d.get("salto_ks_metadata")) if d.get("salto_ks_metadata") is not None else None ), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class AccountReauthorizationRequestedWarning(ResourceMapping): + """Indicates that the Connected Account requires reauthorization using a new Connect Webview. The account is still connected, but cannot access new features. Delaying reauthorization too long will eventually cause the Connected Account to become disconnected. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["account_reauthorization_requested"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class BeingDeletedWarning(ResourceMapping): + """Indicates that the connected account is currently being deleted. All devices, access codes, and other resources associated with this account are in the process of being removed from Seam. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ProviderServiceUnavailableWarning(ResourceMapping): + """Indicates that the connected account's provider service is temporarily unavailable. Seam will automatically retry and reconnect when the service becomes available again. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["provider_service_unavailable"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SetupRequiredWarning(ResourceMapping): + """Indicates that the connected account requires additional setup before it can be fully operational. Follow the instructions in the warning message to complete the setup. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["setup_required"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DormakabaSitesUnapprovedWarning(ResourceMapping): + """Indicates that one or more dormakaba sites associated with the connected account are not approved. Contact support@getseam.com to finish setting up your account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["dormakaba_sites_unapproved"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), ) - accepted_capabilities: List[str] + Errors = Union[ + AccountDisconnectedError, + BridgeDisconnectedError, + SaltoKsSubscriptionLimitExceededError, + DormakabaSitesDisconnectedError, + ] + _ErrorsVariants = { + "account_disconnected": AccountDisconnectedError, + "bridge_disconnected": BridgeDisconnectedError, + "salto_ks_subscription_limit_exceeded": SaltoKsSubscriptionLimitExceededError, + "dormakaba_sites_disconnected": DormakabaSitesDisconnectedError, + } + + Warnings = Union[ + ScheduledMaintenanceWindowWarning, + UnknownIssueWithConnectedAccountWarning, + SaltoKsSubscriptionLimitAlmostReachedWarning, + AccountReauthorizationRequestedWarning, + BeingDeletedWarning, + ProviderServiceUnavailableWarning, + SetupRequiredWarning, + DormakabaSitesUnapprovedWarning, + ] + _WarningsVariants = { + "scheduled_maintenance_window": ScheduledMaintenanceWindowWarning, + "unknown_issue_with_connected_account": UnknownIssueWithConnectedAccountWarning, + "salto_ks_subscription_limit_almost_reached": SaltoKsSubscriptionLimitAlmostReachedWarning, + "account_reauthorization_requested": AccountReauthorizationRequestedWarning, + "being_deleted": BeingDeletedWarning, + "provider_service_unavailable": ProviderServiceUnavailableWarning, + "setup_required": SetupRequiredWarning, + "dormakaba_sites_unapproved": DormakabaSitesUnapprovedWarning, + } + + accepted_capabilities: List[ + Literal["lock", "thermostat", "noise_sensor", "access_control", "camera"] + ] account_type: Optional[str] account_type_display_name: str automatically_manage_new_devices: bool @@ -272,7 +569,10 @@ def from_dict(cls, d: Any): default_checkin_time=d.get("default_checkin_time", None), default_checkout_time=d.get("default_checkout_time", None), display_name=d.get("display_name", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], ical_feed_origin=d.get("ical_feed_origin", None), ical_url=d.get("ical_url", None), image_url=d.get("image_url", None), @@ -282,5 +582,8 @@ def from_dict(cls, d: Any): if d.get("user_identifier") is not None else None ), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], ) diff --git a/seam/resources/device.py b/seam/resources/device.py index ad04335a..34f0ab02 100644 --- a/seam/resources/device.py +++ b/seam/resources/device.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class Device: """Represents a `device `_ that has been connected to Seam. @@ -134,28 +141,25 @@ def from_dict(cls, d: Any): ) @dataclass - class Errors(ResourceMapping): - """Array of errors associated with the device. Each error object within the array contains two fields: ``error_code`` and ``message``. ``error_code`` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. ``message`` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + class AccountDisconnectedError(ResourceMapping): + """Indicates that the account is disconnected. :ivar created_at: Date and time at which Seam created the error. :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar is_connected_account_error: + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar is_device_error: + :ivar is_device_error: Indicates that the error is not a device error. :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. """ created_at: str - error_code: str - is_connected_account_error: Optional[bool] - is_device_error: Optional[Literal[False, True]] + error_code: Literal["account_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] message: str - is_bridge_error: Optional[bool] @classmethod def from_dict(cls, d: Any): @@ -165,181 +169,547 @@ def from_dict(cls, d: Any): is_connected_account_error=d.get("is_connected_account_error", None), is_device_error=d.get("is_device_error", None), message=d.get("message", None), - is_bridge_error=d.get("is_bridge_error", None), ) @dataclass - class Location(ResourceMapping): - """Location information for the device. + class SaltoKsSubscriptionLimitExceededError(ResourceMapping): + """Indicates that the Salto site user limit has been reached. - :ivar location_name: Name of the device location. + :ivar created_at: Date and time at which Seam created the error. - :ivar room_name: Name of the room within the device location, when the provider reports one. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar time_zone: Time zone of the device location. + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. """ - location_name: Optional[str] - room_name: Optional[str] - time_zone: Optional[str] - timezone: Optional[str] + created_at: str + error_code: Literal["salto_ks_subscription_limit_exceeded"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str @classmethod def from_dict(cls, d: Any): return cls( - location_name=d.get("location_name", None), - room_name=d.get("room_name", None), - time_zone=d.get("time_zone", None), - timezone=d.get("timezone", None), + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), ) @dataclass - class Properties(ResourceMapping): - """Properties of the device. + class InsufficientPermissionsError(ResourceMapping): + """Indicates that Seam's integration user does not have sufficient permissions on the provider's system to which this device belongs, so Seam cannot manage access codes or unlock the device. See the error message for specifics, then either reauthorize the connected account in Seam or grant the integration user the required permissions in the provider's system. - :ivar accessory_keypad: Accessory keypad properties and state. + :ivar created_at: Date and time at which Seam created the error. - :ivar appearance: Appearance-related properties, as reported by the device. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar battery: Represents the current status of the battery charge level. + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar battery_level: Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + :ivar is_device_error: Indicates that the error is not a device error. - :ivar currently_triggering_noise_threshold_ids: Array of noise threshold IDs that are currently triggering. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar has_direct_power: Indicates whether the device has direct power. + created_at: str + error_code: Literal["insufficient_permissions"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str - :ivar image_alt_text: Alt text for the device image. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar image_url: Image URL for the device. + @dataclass + class DormakabaSitesDisconnectedError(ResourceMapping): + """Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. - :ivar manufacturer: Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. + :ivar created_at: Date and time at which Seam created the error. - :ivar model: Device model-related properties. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar name: Deprecated: use device.display_name instead Name of the device. + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar noise_level_decibels: Indicates current noise level in decibels, if the device supports noise detection. + :ivar is_device_error: Indicates that the error is not a device error. - :ivar offline_access_codes_enabled: Deprecated: use device.can_program_offline_access_codes Indicates whether it is currently possible to use offline access codes for the device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar online: Indicates whether the device is online. + created_at: str + error_code: Literal["dormakaba_sites_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str - :ivar online_access_codes_enabled: Deprecated: use device.can_program_online_access_codes Indicates whether it is currently possible to use online access codes for the device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar serial_number: Serial number of the device. + @dataclass + class DeviceOfflineError(ResourceMapping): + """Indicates that the device is offline. - :ivar supports_accessory_keypad: Deprecated: use device.properties.model.can_connect_accessory_keypad + :ivar created_at: Date and time at which Seam created the error. - :ivar supports_offline_access_codes: Deprecated: use offline_access_codes_enabled + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar assa_abloy_credential_service_metadata: ASSA ABLOY Credential Service metadata for the phone. + :ivar is_device_error: Indicates that the error is a device error. - :ivar salto_space_credential_service_metadata: Salto Space credential service metadata for the phone. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar akiles_metadata: Metadata for an Akiles device. + created_at: str + error_code: Literal["device_offline"] + is_device_error: Literal[True] + message: str - :ivar aqara_metadata: Metadata for an Aqara device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar assa_abloy_vostio_metadata: Metadata for an ASSA ABLOY Vostio system. + @dataclass + class DeviceRemovedError(ResourceMapping): + """Indicates that the device has been removed. - :ivar august_metadata: Metadata for an August device. + :ivar created_at: Date and time at which Seam created the error. - :ivar avigilon_alta_metadata: Metadata for an Avigilon Alta system. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar brivo_metadata: Metadata for a Brivo device. + :ivar is_device_error: Indicates that the error is a device error. - :ivar controlbyweb_metadata: Metadata for a ControlByWeb device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar dormakaba_oracode_metadata: Metadata for a dormakaba Oracode device. + created_at: str + error_code: Literal["device_removed"] + is_device_error: Literal[True] + message: str - :ivar ecobee_metadata: Metadata for an ecobee device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar four_suites_metadata: Metadata for a 4SUITES device. + @dataclass + class HubDisconnectedError(ResourceMapping): + """Indicates that the hub is disconnected. - :ivar genie_metadata: Metadata for a Genie device. + :ivar created_at: Date and time at which Seam created the error. - :ivar honeywell_resideo_metadata: Metadata for a Honeywell Resideo device. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar igloo_metadata: Metadata for an igloo device. + :ivar is_device_error: Indicates that the error is a device error. - :ivar igloohome_metadata: Metadata for an igloohome device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar keynest_metadata: Metadata for a KeyNest device. + created_at: str + error_code: Literal["hub_disconnected"] + is_device_error: Literal[True] + message: str - :ivar kisi_metadata: Metadata for a Kisi device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar korelock_metadata: Metadata for a Korelock device. + @dataclass + class DeviceDisconnectedError(ResourceMapping): + """Indicates that the device is disconnected. - :ivar kwikset_metadata: Metadata for a Kwikset device. + :ivar created_at: Date and time at which Seam created the error. - :ivar lockly_metadata: Metadata for a Lockly device. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar minut_metadata: Metadata for a Minut device. + :ivar is_device_error: Indicates that the error is a device error. - :ivar nest_metadata: Metadata for a Google Nest device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar noiseaware_metadata: Metadata for a NoiseAware device. + created_at: str + error_code: Literal["device_disconnected"] + is_device_error: Literal[True] + message: str - :ivar nuki_metadata: Metadata for a Nuki device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar omnitec_metadata: Metadata for an Omnitec device. + @dataclass + class EmptyBackupAccessCodePoolError(ResourceMapping): + """Indicates that the `backup access code pool `_ is empty. - :ivar ring_metadata: Metadata for a Ring device. + :ivar created_at: Date and time at which Seam created the error. - :ivar salto_ks_metadata: Metadata for a Salto KS device. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar salto_metadata: Deprecated: Use ``salto_ks_metadata`` instead. Metada for a Salto device. + :ivar is_device_error: Indicates that the error is a device error. - :ivar schlage_metadata: Metadata for a Schlage device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar seam_bridge_metadata: Metadata for Seam Bridge. + created_at: str + error_code: Literal["empty_backup_access_code_pool"] + is_device_error: Literal[True] + message: str - :ivar sensi_metadata: Metadata for a Sensi device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar smartthings_metadata: Metadata for a SmartThings device. + @dataclass + class AugustLockNotAuthorizedError(ResourceMapping): + """Indicates that the user is not authorized to use the August lock. - :ivar tado_metadata: Metadata for a tado° device. + :ivar created_at: Date and time at which Seam created the error. - :ivar tedee_metadata: Metadata for a Tedee device. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar ttlock_metadata: Metadata for a TTLock device. + :ivar is_device_error: Indicates that the error is a device error. - :ivar two_n_metadata: Metadata for a 2N device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar ultraloq_metadata: Metadata for an Ultraloq device. + created_at: str + error_code: Literal["august_lock_not_authorized"] + is_device_error: Literal[True] + message: str - :ivar visionline_metadata: Metadata for an ASSA ABLOY Visionline system. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar wyze_metadata: Metadata for a Wyze device. + @dataclass + class MissingDeviceCredentialsError(ResourceMapping): + """Indicates that device credentials are missing. - :ivar yacan_metadata: Metadata for a Yacan device. + :ivar created_at: Date and time at which Seam created the error. - :ivar auto_lock_delay_seconds: The delay in seconds before the lock automatically locks after being unlocked. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar auto_lock_enabled: Indicates whether automatic locking is enabled. + :ivar is_device_error: Indicates that the error is a device error. - :ivar backup_access_code_pool_enabled: Indicates whether the `backup access code pool `_ is currently enabled for the device. To disable it, set this to ``false`` using `/devices/update `_. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar code_constraints: Constraints on access codes for the device. Seam represents each constraint as an object with a ``constraint_type`` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. + created_at: str + error_code: Literal["missing_device_credentials"] + is_device_error: Literal[True] + message: str - :ivar door_open: Indicates whether the door is open. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar has_native_entry_events: Indicates whether the device supports native entry events. + @dataclass + class AuxiliaryHeatRunningError(ResourceMapping): + """Indicates that the auxiliary heat is running. - :ivar keypad_battery: Keypad battery status. + :ivar created_at: Date and time at which Seam created the error. - :ivar locked: Indicates whether the lock is locked. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar max_active_codes_supported: Maximum number of active access codes that the device supports. + :ivar is_device_error: Indicates that the error is a device error. - :ivar offline_time_frame_options: Time frames that may be requested when creating an offline access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by ``display_name`` when they do) and satisfies that one option's rules. When ``undefined``, any time frame works. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar online_time_frame_options: Time frames that may be requested when creating an online access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by ``display_name`` when they do) and satisfies that one option's rules. When ``undefined``, any time frame works. + created_at: str + error_code: Literal["auxiliary_heat_running"] + is_device_error: Literal[True] + message: str - :ivar supported_code_lengths: Supported code lengths for access codes. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class SubscriptionRequiredError(ResourceMapping): + """Indicates that a subscription is required to connect. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["subscription_required"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class BridgeDisconnectedError(ResourceMapping): + """Indicates that the Seam API cannot communicate with `Seam Bridge `_, for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also `Troubleshooting Your Access Control System `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + + :ivar is_connected_account_error: Indicates whether the error is related specifically to the connected account. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["bridge_disconnected"] + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_bridge_error=d.get("is_bridge_error", None), + is_connected_account_error=d.get("is_connected_account_error", None), + message=d.get("message", None), + ) + + @dataclass + class Location(ResourceMapping): + """Location information for the device. + + :ivar location_name: Name of the device location. + + :ivar room_name: Name of the room within the device location, when the provider reports one. + + :ivar time_zone: Time zone of the device location. + + :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. + """ + + location_name: Optional[str] + room_name: Optional[str] + time_zone: Optional[str] + timezone: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + location_name=d.get("location_name", None), + room_name=d.get("room_name", None), + time_zone=d.get("time_zone", None), + timezone=d.get("timezone", None), + ) + + @dataclass + class Properties(ResourceMapping): + """Properties of the device. + + :ivar accessory_keypad: Accessory keypad properties and state. + + :ivar appearance: Appearance-related properties, as reported by the device. + + :ivar battery: Represents the current status of the battery charge level. + + :ivar battery_level: Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + + :ivar currently_triggering_noise_threshold_ids: Array of noise threshold IDs that are currently triggering. + + :ivar has_direct_power: Indicates whether the device has direct power. + + :ivar image_alt_text: Alt text for the device image. + + :ivar image_url: Image URL for the device. + + :ivar manufacturer: Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. + + :ivar model: Device model-related properties. + + :ivar name: Deprecated: use device.display_name instead Name of the device. + + :ivar noise_level_decibels: Indicates current noise level in decibels, if the device supports noise detection. + + :ivar offline_access_codes_enabled: Deprecated: use device.can_program_offline_access_codes Indicates whether it is currently possible to use offline access codes for the device. + + :ivar online: Indicates whether the device is online. + + :ivar online_access_codes_enabled: Deprecated: use device.can_program_online_access_codes Indicates whether it is currently possible to use online access codes for the device. + + :ivar serial_number: Serial number of the device. + + :ivar supports_accessory_keypad: Deprecated: use device.properties.model.can_connect_accessory_keypad + + :ivar supports_offline_access_codes: Deprecated: use offline_access_codes_enabled + + :ivar assa_abloy_credential_service_metadata: ASSA ABLOY Credential Service metadata for the phone. + + :ivar salto_space_credential_service_metadata: Salto Space credential service metadata for the phone. + + :ivar akiles_metadata: Metadata for an Akiles device. + + :ivar aqara_metadata: Metadata for an Aqara device. + + :ivar assa_abloy_vostio_metadata: Metadata for an ASSA ABLOY Vostio system. + + :ivar august_metadata: Metadata for an August device. + + :ivar avigilon_alta_metadata: Metadata for an Avigilon Alta system. + + :ivar brivo_metadata: Metadata for a Brivo device. + + :ivar controlbyweb_metadata: Metadata for a ControlByWeb device. + + :ivar dormakaba_oracode_metadata: Metadata for a dormakaba Oracode device. + + :ivar ecobee_metadata: Metadata for an ecobee device. + + :ivar four_suites_metadata: Metadata for a 4SUITES device. + + :ivar genie_metadata: Metadata for a Genie device. + + :ivar honeywell_resideo_metadata: Metadata for a Honeywell Resideo device. + + :ivar igloo_metadata: Metadata for an igloo device. + + :ivar igloohome_metadata: Metadata for an igloohome device. + + :ivar keynest_metadata: Metadata for a KeyNest device. + + :ivar kisi_metadata: Metadata for a Kisi device. + + :ivar korelock_metadata: Metadata for a Korelock device. + + :ivar kwikset_metadata: Metadata for a Kwikset device. + + :ivar lockly_metadata: Metadata for a Lockly device. + + :ivar minut_metadata: Metadata for a Minut device. + + :ivar nest_metadata: Metadata for a Google Nest device. + + :ivar noiseaware_metadata: Metadata for a NoiseAware device. + + :ivar nuki_metadata: Metadata for a Nuki device. + + :ivar omnitec_metadata: Metadata for an Omnitec device. + + :ivar ring_metadata: Metadata for a Ring device. + + :ivar salto_ks_metadata: Metadata for a Salto KS device. + + :ivar salto_metadata: Deprecated: Use ``salto_ks_metadata`` instead. Metada for a Salto device. + + :ivar schlage_metadata: Metadata for a Schlage device. + + :ivar seam_bridge_metadata: Metadata for Seam Bridge. + + :ivar sensi_metadata: Metadata for a Sensi device. + + :ivar smartthings_metadata: Metadata for a SmartThings device. + + :ivar tado_metadata: Metadata for a tado° device. + + :ivar tedee_metadata: Metadata for a Tedee device. + + :ivar ttlock_metadata: Metadata for a TTLock device. + + :ivar two_n_metadata: Metadata for a 2N device. + + :ivar ultraloq_metadata: Metadata for an Ultraloq device. + + :ivar visionline_metadata: Metadata for an ASSA ABLOY Visionline system. + + :ivar wyze_metadata: Metadata for a Wyze device. + + :ivar yacan_metadata: Metadata for a Yacan device. + + :ivar auto_lock_delay_seconds: The delay in seconds before the lock automatically locks after being unlocked. + + :ivar auto_lock_enabled: Indicates whether automatic locking is enabled. + + :ivar backup_access_code_pool_enabled: Indicates whether the `backup access code pool `_ is currently enabled for the device. To disable it, set this to ``false`` using `/devices/update `_. + + :ivar code_constraints: Constraints on access codes for the device. Seam represents each constraint as an object with a ``constraint_type`` property. Depending on the constraint type, there may also be additional properties. Note that some constraints are manufacturer- or device-specific. + + :ivar door_open: Indicates whether the door is open. + + :ivar has_native_entry_events: Indicates whether the device supports native entry events. + + :ivar keypad_battery: Keypad battery status. + + :ivar locked: Indicates whether the lock is locked. + + :ivar max_active_codes_supported: Maximum number of active access codes that the device supports. + + :ivar offline_time_frame_options: Time frames that may be requested when creating an offline access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by ``display_name`` when they do) and satisfies that one option's rules. When ``undefined``, any time frame works. + + :ivar online_time_frame_options: Time frames that may be requested when creating an online access code, expressed as a list of options. The caller picks one option (by matching the requested duration when the options' duration ranges do not overlap, or by ``display_name`` when they do) and satisfies that one option's rules. When ``undefined``, any time frame works. + + :ivar supported_code_lengths: Supported code lengths for access codes. :ivar supports_backup_access_code_pool: Indicates whether the device supports a `backup access code pool `_. @@ -472,7 +842,7 @@ class Battery(ResourceMapping): """ level: float - status: str + status: Literal["critical", "low", "good", "full"] @classmethod def from_dict(cls, d: Any): @@ -1439,7 +1809,7 @@ class NoiseawareMetadata(ResourceMapping): """ device_id: Optional[str] - device_model: Optional[str] + device_model: Optional[Literal["indoor", "outdoor"]] device_name: Optional[str] noise_level_decibel: Optional[float] noise_level_nrs: Optional[float] @@ -1668,7 +2038,7 @@ class SeamBridgeMetadata(ResourceMapping): device_num: Optional[float] name: Optional[str] - unlock_method: Optional[str] + unlock_method: Optional[Literal["bridge", "doorking"]] @classmethod def from_dict(cls, d: Any): @@ -2034,7 +2404,22 @@ class CodeConstraints(ResourceMapping): :ivar min_length: Minimum name length constraint for access codes.""" - constraint_type: str + constraint_type: Literal[ + "no_zeros", + "cannot_start_with_12", + "no_triple_consecutive_ints", + "cannot_specify_pin_code", + "pin_code_matches_existing_set", + "start_date_in_future", + "no_ascending_or_descending_sequence", + "at_least_three_unique_digits", + "cannot_contain_089", + "cannot_contain_0789", + "unique_first_four_digits", + "no_all_same_digits", + "name_length", + "name_must_be_unique", + ] max_length: Optional[float] min_length: Optional[float] @@ -2328,7 +2713,7 @@ class EcobeeMetadata(ResourceMapping): climate_ref: Optional[str] is_optimized: Optional[bool] - owner: Optional[str] + owner: Optional[Literal["user", "system"]] @classmethod def from_dict(cls, d: Any): @@ -2342,15 +2727,19 @@ def from_dict(cls, d: Any): can_edit: bool can_use_with_thermostat_daily_programs: bool climate_preset_key: str - climate_preset_mode: Optional[str] + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] cooling_set_point_celsius: Optional[float] cooling_set_point_fahrenheit: Optional[float] display_name: str ecobee_metadata: Optional[EcobeeMetadata] - fan_mode_setting: Optional[str] + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] heating_set_point_celsius: Optional[float] heating_set_point_fahrenheit: Optional[float] - hvac_mode_setting: Optional[str] + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] manual_override_allowed: bool name: Optional[str] @@ -2432,7 +2821,7 @@ class EcobeeMetadata(ResourceMapping): climate_ref: Optional[str] is_optimized: Optional[bool] - owner: Optional[str] + owner: Optional[Literal["user", "system"]] @classmethod def from_dict(cls, d: Any): @@ -2446,15 +2835,19 @@ def from_dict(cls, d: Any): can_edit: Optional[bool] can_use_with_thermostat_daily_programs: Optional[bool] climate_preset_key: Optional[str] - climate_preset_mode: Optional[str] + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] cooling_set_point_celsius: Optional[float] cooling_set_point_fahrenheit: Optional[float] display_name: Optional[str] ecobee_metadata: Optional[EcobeeMetadata] - fan_mode_setting: Optional[str] + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] heating_set_point_celsius: Optional[float] heating_set_point_fahrenheit: Optional[float] - hvac_mode_setting: Optional[str] + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] manual_override_allowed: Optional[bool] name: Optional[str] @@ -2536,7 +2929,7 @@ class EcobeeMetadata(ResourceMapping): climate_ref: Optional[str] is_optimized: Optional[bool] - owner: Optional[str] + owner: Optional[Literal["user", "system"]] @classmethod def from_dict(cls, d: Any): @@ -2550,15 +2943,19 @@ def from_dict(cls, d: Any): can_edit: Optional[bool] can_use_with_thermostat_daily_programs: Optional[bool] climate_preset_key: Optional[str] - climate_preset_mode: Optional[str] + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] cooling_set_point_celsius: Optional[float] cooling_set_point_fahrenheit: Optional[float] display_name: Optional[str] ecobee_metadata: Optional[EcobeeMetadata] - fan_mode_setting: Optional[str] + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] heating_set_point_celsius: Optional[float] heating_set_point_fahrenheit: Optional[float] - hvac_mode_setting: Optional[str] + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] manual_override_allowed: Optional[bool] name: Optional[str] @@ -2796,14 +3193,18 @@ def from_dict(cls, d: Any): supports_backup_access_code_pool: Optional[bool] active_thermostat_schedule: Optional[ActiveThermostatSchedule] active_thermostat_schedule_id: Optional[str] - available_climate_preset_modes: Optional[List[str]] + available_climate_preset_modes: Optional[ + List[Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"]] + ] available_climate_presets: Optional[List[AvailableClimatePresets]] - available_fan_mode_settings: Optional[List[str]] - available_hvac_mode_settings: Optional[List[str]] + available_fan_mode_settings: Optional[List[Literal["auto", "on", "circulate"]]] + available_hvac_mode_settings: Optional[ + List[Literal["off", "heat", "cool", "heat_cool", "eco"]] + ] current_climate_setting: Optional[CurrentClimateSetting] default_climate_setting: Optional[DefaultClimateSetting] fallback_climate_preset_key: Optional[str] - fan_mode_setting: Optional[str] + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] is_cooling: Optional[bool] is_fan_running: Optional[bool] is_heating: Optional[bool] @@ -3220,25 +3621,42 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Array of warnings associated with the device. Each warning object within the array contains two fields: ``warning_code`` and ``message``. ``warning_code`` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. ``message`` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + class PartialBackupAccessCodePoolWarning(ResourceMapping): + """Indicates that the backup access code is unhealthy. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ - :ivar active_access_code_count: Number of active access codes on the device when the warning was set. + created_at: str + message: str + warning_code: Literal["partial_backup_access_code_pool"] - :ivar max_active_access_code_count: Maximum number of active access codes supported by the device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ManyActiveBackupCodesWarning(ResourceMapping): + """Indicates that there are too many backup codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. """ created_at: str message: str - warning_code: str - active_access_code_count: Optional[int] - max_active_access_code_count: Optional[int] + warning_code: Literal["many_active_backup_codes"] @classmethod def from_dict(cls, d: Any): @@ -3246,19 +3664,692 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), message=d.get("message", None), warning_code=d.get("warning_code", None), - active_access_code_count=d.get("active_access_code_count", None), - max_active_access_code_count=d.get( - "max_active_access_code_count", None - ), ) - can_configure_auto_lock: Optional[bool] - can_hvac_cool: Optional[bool] - can_hvac_heat: Optional[bool] - can_hvac_heat_cool: Optional[bool] - can_program_offline_access_codes: Optional[bool] - can_program_online_access_codes: Optional[bool] - can_program_thermostat_programs_as_different_each_day: Optional[bool] + @dataclass + class ThirdPartyIntegrationDetectedWarning(ResourceMapping): + """Indicates that a third-party integration has been detected. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["third_party_integration_detected"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TtlockLockGatewayUnlockingNotEnabledWarning(ResourceMapping): + """Indicates that the Remote Unlock feature is not enabled in the settings." + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["ttlock_lock_gateway_unlocking_not_enabled"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TtlockWeakGatewaySignalWarning(ResourceMapping): + """Indicates that the gateway signal is weak. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["ttlock_weak_gateway_signal"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class PowerSavingModeWarning(ResourceMapping): + """Indicates that the device is in power saving mode and may have limited functionality. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["power_saving_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TemperatureThresholdExceededWarning(ResourceMapping): + """Indicates that the temperature threshold has been exceeded. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["temperature_threshold_exceeded"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceCommunicationDegradedWarning(ResourceMapping): + """Indicates that the device appears to be unresponsive. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["device_communication_degraded"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ScheduledMaintenanceWindowWarning(ResourceMapping): + """Indicates that a scheduled maintenance window has been detected. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["scheduled_maintenance_window"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceHasFlakyConnectionWarning(ResourceMapping): + """Indicates that the device has a flaky connection. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["device_has_flaky_connection"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsOfficeModeWarning(ResourceMapping): + """Indicates that the Salto KS lock is in Office Mode. Access Codes will not unlock doors. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_office_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsPrivacyModeWarning(ResourceMapping): + """Indicates that the Salto KS lock is in Privacy Mode. Access Codes will not unlock doors. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_privacy_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class PrivacyModeWarning(ResourceMapping): + """Indicates that the lock is in Privacy Mode. Access codes and remote unlock are blocked until Privacy Mode is disabled. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["privacy_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitAlmostReachedWarning(ResourceMapping): + """Indicates that the Salto KS site has exceeded 80% of the maximum number of allowed users. Increase your subscription limit or delete some users from your site. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_subscription_limit_almost_reached"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsLockAccessCodeSupportRemovedWarning(ResourceMapping): + """Indicates that a change in the reported device model has been detected for this Salto KS lock, which may occur after an IQ hub reset. Access code support may be affected. See https://help.getseam.com/articles/5098842588-salto-ks-lock-loses-access-code-support for troubleshooting steps. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_lock_access_code_support_removed"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithPhoneWarning(ResourceMapping): + """Indicates that an unknown issue occurred while syncing the state of the phone with the provider. This issue may affect the proper functioning of the phone. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["unknown_issue_with_phone"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class LocklyTimeZoneNotConfiguredWarning(ResourceMapping): + """Indicates that Seam detected that the Lockly device does not have a time zone configured. Time-bound codes may not work as expected. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["lockly_time_zone_not_configured"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UltraloqTimeZoneUnknownWarning(ResourceMapping): + """Indicates that Seam does not know the time zone of the Ultraloq device. Set a time zone to enable time-bound access codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["ultraloq_time_zone_unknown"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TimeZoneUnknownWarning(ResourceMapping): + """Indicates that Seam does not know the device's time zone. Set a time zone to enable time-bound access codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["time_zone_unknown"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TimeZoneMismatchWarning(ResourceMapping): + """Indicates that the device's configured time zone does not match its hardware UTC offset. Time-bound access codes may activate at the wrong local time. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["time_zone_mismatch"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TwoNDeviceMissingTimezoneWarning(ResourceMapping): + """Indicates that the 2N device does not have a time zone configured. Configure a time zone on the device to enable access codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["two_n_device_missing_timezone"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class HubRequiredForAdditionalCapabilitiesWarning(ResourceMapping): + """Indicates that a hub or relay must be connected to unlock additional capabilities such as remote unlock. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["hub_required_for_additional_capabilities"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ProviderIssueWarning(ResourceMapping): + """Indicates a provider-specific issue that may affect device functionality. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["provider_issue"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class KeynestUnsupportedLockerWarning(ResourceMapping): + """Indicates that the key is in a locker that does not support the access codes API. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["keynest_unsupported_locker"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class AccessoryKeypadSetupRequiredWarning(ResourceMapping): + """Indicates that the accessory keypad exists, but is not linked to the Igloohome Bridge. Online access code programming will fail until the keypad is linked to the Igloohome Bridge in the Igloohome app. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["accessory_keypad_setup_required"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnreliableOnlineStatusWarning(ResourceMapping): + """Indicates that the device may optimistically be reported as online because the provider does not reliably report its online status. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["unreliable_online_status"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class MaxAccessCodesReachedWarning(ResourceMapping): + """Indicates that the device has reached its maximum number of active access codes. Delete existing codes before creating new ones. + + :ivar active_access_code_count: Number of active access codes on the device when the warning was set. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar max_active_access_code_count: Maximum number of active access codes supported by the device. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + active_access_code_count: int + created_at: str + max_active_access_code_count: int + message: str + warning_code: Literal["max_access_codes_reached"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + active_access_code_count=d.get("active_access_code_count", None), + created_at=d.get("created_at", None), + max_active_access_code_count=d.get( + "max_active_access_code_count", None + ), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + Errors = Union[ + AccountDisconnectedError, + SaltoKsSubscriptionLimitExceededError, + InsufficientPermissionsError, + DormakabaSitesDisconnectedError, + DeviceOfflineError, + DeviceRemovedError, + HubDisconnectedError, + DeviceDisconnectedError, + EmptyBackupAccessCodePoolError, + AugustLockNotAuthorizedError, + MissingDeviceCredentialsError, + AuxiliaryHeatRunningError, + SubscriptionRequiredError, + BridgeDisconnectedError, + ] + _ErrorsVariants = { + "account_disconnected": AccountDisconnectedError, + "salto_ks_subscription_limit_exceeded": SaltoKsSubscriptionLimitExceededError, + "insufficient_permissions": InsufficientPermissionsError, + "dormakaba_sites_disconnected": DormakabaSitesDisconnectedError, + "device_offline": DeviceOfflineError, + "device_removed": DeviceRemovedError, + "hub_disconnected": HubDisconnectedError, + "device_disconnected": DeviceDisconnectedError, + "empty_backup_access_code_pool": EmptyBackupAccessCodePoolError, + "august_lock_not_authorized": AugustLockNotAuthorizedError, + "missing_device_credentials": MissingDeviceCredentialsError, + "auxiliary_heat_running": AuxiliaryHeatRunningError, + "subscription_required": SubscriptionRequiredError, + "bridge_disconnected": BridgeDisconnectedError, + } + + Warnings = Union[ + PartialBackupAccessCodePoolWarning, + ManyActiveBackupCodesWarning, + ThirdPartyIntegrationDetectedWarning, + TtlockLockGatewayUnlockingNotEnabledWarning, + TtlockWeakGatewaySignalWarning, + PowerSavingModeWarning, + TemperatureThresholdExceededWarning, + DeviceCommunicationDegradedWarning, + ScheduledMaintenanceWindowWarning, + DeviceHasFlakyConnectionWarning, + SaltoKsOfficeModeWarning, + SaltoKsPrivacyModeWarning, + PrivacyModeWarning, + SaltoKsSubscriptionLimitAlmostReachedWarning, + SaltoKsLockAccessCodeSupportRemovedWarning, + UnknownIssueWithPhoneWarning, + LocklyTimeZoneNotConfiguredWarning, + UltraloqTimeZoneUnknownWarning, + TimeZoneUnknownWarning, + TimeZoneMismatchWarning, + TwoNDeviceMissingTimezoneWarning, + HubRequiredForAdditionalCapabilitiesWarning, + ProviderIssueWarning, + KeynestUnsupportedLockerWarning, + AccessoryKeypadSetupRequiredWarning, + UnreliableOnlineStatusWarning, + MaxAccessCodesReachedWarning, + ] + _WarningsVariants = { + "partial_backup_access_code_pool": PartialBackupAccessCodePoolWarning, + "many_active_backup_codes": ManyActiveBackupCodesWarning, + "third_party_integration_detected": ThirdPartyIntegrationDetectedWarning, + "ttlock_lock_gateway_unlocking_not_enabled": TtlockLockGatewayUnlockingNotEnabledWarning, + "ttlock_weak_gateway_signal": TtlockWeakGatewaySignalWarning, + "power_saving_mode": PowerSavingModeWarning, + "temperature_threshold_exceeded": TemperatureThresholdExceededWarning, + "device_communication_degraded": DeviceCommunicationDegradedWarning, + "scheduled_maintenance_window": ScheduledMaintenanceWindowWarning, + "device_has_flaky_connection": DeviceHasFlakyConnectionWarning, + "salto_ks_office_mode": SaltoKsOfficeModeWarning, + "salto_ks_privacy_mode": SaltoKsPrivacyModeWarning, + "privacy_mode": PrivacyModeWarning, + "salto_ks_subscription_limit_almost_reached": SaltoKsSubscriptionLimitAlmostReachedWarning, + "salto_ks_lock_access_code_support_removed": SaltoKsLockAccessCodeSupportRemovedWarning, + "unknown_issue_with_phone": UnknownIssueWithPhoneWarning, + "lockly_time_zone_not_configured": LocklyTimeZoneNotConfiguredWarning, + "ultraloq_time_zone_unknown": UltraloqTimeZoneUnknownWarning, + "time_zone_unknown": TimeZoneUnknownWarning, + "time_zone_mismatch": TimeZoneMismatchWarning, + "two_n_device_missing_timezone": TwoNDeviceMissingTimezoneWarning, + "hub_required_for_additional_capabilities": HubRequiredForAdditionalCapabilitiesWarning, + "provider_issue": ProviderIssueWarning, + "keynest_unsupported_locker": KeynestUnsupportedLockerWarning, + "accessory_keypad_setup_required": AccessoryKeypadSetupRequiredWarning, + "unreliable_online_status": UnreliableOnlineStatusWarning, + "max_access_codes_reached": MaxAccessCodesReachedWarning, + } + + can_configure_auto_lock: Optional[bool] + can_hvac_cool: Optional[bool] + can_hvac_heat: Optional[bool] + can_hvac_heat_cool: Optional[bool] + can_program_offline_access_codes: Optional[bool] + can_program_online_access_codes: Optional[bool] + can_program_thermostat_programs_as_different_each_day: Optional[bool] can_program_thermostat_programs_as_same_each_day: Optional[bool] can_program_thermostat_programs_as_weekday_weekend: Optional[bool] can_remotely_lock: Optional[bool] @@ -3272,14 +4363,62 @@ def from_dict(cls, d: Any): can_simulate_removal: Optional[bool] can_turn_off_hvac: Optional[bool] can_unlock_with_code: Optional[bool] - capabilities_supported: List[str] + capabilities_supported: List[ + Literal[ + "access_code", "lock", "noise_detection", "thermostat", "battery", "phone" + ] + ] connected_account_id: str created_at: str custom_metadata: Dict[str, Union[str, bool]] device_id: str device_manufacturer: Optional[DeviceManufacturer] device_provider: Optional[DeviceProvider] - device_type: str + device_type: Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] display_name: str errors: List[Errors] is_managed: Literal[True] @@ -3344,7 +4483,10 @@ def from_dict(cls, d: Any): ), device_type=d.get("device_type", None), display_name=d.get("display_name", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], is_managed=d.get("is_managed", None), location=( cls.Location.from_dict(d.get("location")) @@ -3358,6 +4500,9 @@ def from_dict(cls, d: Any): else None ), space_ids=d.get("space_ids", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/device_provider.py b/seam/resources/device_provider.py index 5d525cdb..b4276458 100644 --- a/seam/resources/device_provider.py +++ b/seam/resources/device_provider.py @@ -77,10 +77,86 @@ class DeviceProvider: can_simulate_removal: Optional[bool] can_turn_off_hvac: Optional[bool] can_unlock_with_code: Optional[bool] - device_provider_name: str + device_provider_name: Literal[ + "hotek", + "dormakaba_community", + "legic_connect", + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "schlage", + "smartthings", + "yale", + "genie", + "doorking", + "salto", + "salto_ks", + "salto_ks_accept", + "lockly", + "ttlock", + "linear", + "noiseaware", + "nuki", + "igloo", + "kwikset", + "minut", + "my_2n", + "controlbyweb", + "nest", + "igloohome", + "ecobee", + "four_suites", + "dormakaba_oracode", + "pti", + "wyze", + "seam_passport", + "visionline", + "assa_abloy_credential_service", + "tedee", + "honeywell_resideo", + "first_alert", + "latch", + "akiles", + "assa_abloy_vostio", + "assa_abloy_vostio_credential_service", + "tado", + "salto_space", + "sensi", + "keynest", + "korelock", + "keyincode", + "dormakaba_ambiance", + "ultraloq", + "yacan", + "dusaw", + "sifely", + "thirty_three_lock", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "aqara", + ] display_name: str image_url: str - provider_categories: List[str] + provider_categories: List[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + ] + ] @classmethod def from_dict(cls, d: Any): diff --git a/seam/resources/phone.py b/seam/resources/phone.py index 2ea4b783..ac5aa27f 100644 --- a/seam/resources/phone.py +++ b/seam/resources/phone.py @@ -164,7 +164,7 @@ def from_dict(cls, d: Any): created_at: str custom_metadata: Dict[str, Union[str, bool]] device_id: str - device_type: str + device_type: Literal["ios_phone", "android_phone"] display_name: str errors: List[Errors] nickname: Optional[str] diff --git a/seam/resources/seam_event.py b/seam/resources/seam_event.py index 045a33d1..2b743985 100644 --- a/seam/resources/seam_event.py +++ b/seam/resources/seam_event.py @@ -1,585 +1,601 @@ -from typing import Any, Dict, List, Literal, Optional, Union +from typing import Any, Dict, List, Literal, Optional, Union, cast from dataclasses import dataclass from ..deep_attr_dict import DeepAttrDict from ..resource_mapping import ResourceMapping @dataclass -class SeamEvent: - """ +class AccessCodeCreatedEvent: + """An `access code `_ was created. - :ivar access_code_id: + :ivar access_code_id: ID of the affected access code. - :ivar connected_account_custom_metadata: + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. - :ivar connected_account_id: + :ivar connected_account_id: ID of the connected account associated with the affected access code. :ivar created_at: Date and time at which the event was created. - :ivar device_custom_metadata: + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. - :ivar device_id: + :ivar device_id: ID of the device associated with the affected access code. :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. :ivar event_id: ID of the event. - :ivar event_type: Type of the event. + :ivar event_type: :ivar occurred_at: Date and time at which the event occurred. - :ivar workspace_id: ID of the workspace associated with the event. - - :ivar change_reason: Human-readable reason for the change (e.g. ``ongoing code auto-renewed``). - - :ivar changed_properties: List of properties that changed on the access code. - - :ivar description: Human-readable description of the change and its source. - - :ivar from_: - - :ivar to: - - :ivar requested_mutations: Array of mutations requested on the access code, each containing the mutation type and from/to values. - - :ivar code: - - :ivar access_code_errors: Errors associated with the access code. - - :ivar access_code_warnings: Warnings associated with the access code. - - :ivar connected_account_errors: Errors associated with the connected account. - - :ivar connected_account_warnings: Warnings associated with the connected account. - - :ivar device_errors: Errors associated with the device. - - :ivar device_warnings: Warnings associated with the device. - - :ivar backup_access_code_id: ID of the backup access code that was pulled from the pool. - - :ivar access_grant_id: ID of the affected Access Grant. - - :ivar acs_entrance_id: - - :ivar access_grant_key: Key of the affected Access Grant (if present). + :ivar workspace_id: ID of the workspace associated with the event.""" - :ivar ends_at: The new end time for the access grant. + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.created"] + occurred_at: str + workspace_id: str - :ivar starts_at: The new start time for the access grant. + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) - :ivar error_message: Description of why the access methods could not be created. - :ivar missing_device_ids: IDs of the devices that did not receive a requested access method. Use these to identify which specific devices failed without having to fetch the Access Grant. +@dataclass +class AccessCodeChangedEvent: + """An `access code `_ was changed. - :ivar access_grant_ids: IDs of the access grants associated with this access method. + :ivar access_code_id: ID of the affected access code. - :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + :ivar change_reason: Human-readable reason for the change (e.g. ``ongoing code auto-renewed``). - :ivar access_method_id: ID of the affected access method. + :ivar changed_properties: List of properties that changed on the access code. - :ivar is_backup_code: Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. - :ivar acs_system_id: ID of the access system. + :ivar connected_account_id: ID of the connected account associated with the affected access code. - :ivar acs_system_errors: Errors associated with the access control system. + :ivar created_at: Date and time at which the event was created. - :ivar acs_system_warnings: Warnings associated with the access control system. + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. - :ivar acs_credential_id: ID of the affected credential. + :ivar device_id: ID of the device associated with the affected access code. - :ivar acs_user_id: ID of the affected access system user. + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. - :ivar acs_encoder_id: ID of the affected encoder. + :ivar event_id: ID of the event. - :ivar acs_access_group_id: ID of the affected access group. + :ivar event_type: - :ivar client_session_id: ID of the affected client session. + :ivar occurred_at: Date and time at which the event occurred. - :ivar connect_webview_id: + :ivar workspace_id: ID of the workspace associated with the event.""" - :ivar customer_key: + @dataclass + class ChangedProperties(ResourceMapping): + """List of properties that changed on the access code. - :ivar action_attempt_id: + :ivar from_: Previous value of the property, or null if not set. - :ivar action_type: Type of the action. + :ivar property: Name of the property that changed (e.g. ``code``). - :ivar status: Status of the action. + :ivar to: New value of the property, or null if cleared.""" - :ivar error_code: Error code associated with the disconnection event, if any. + from_: Optional[str] + property: str + to: Optional[str] - :ivar battery_level: Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + @classmethod + def from_dict(cls, d: Any): + return cls( + from_=d.get("from", None), + property=d.get("property", None), + to=d.get("to", None), + ) - :ivar battery_status: Battery status of the affected device, calculated from the numeric ``battery_level`` value. + access_code_id: str + change_reason: Optional[str] + changed_properties: Optional[List[ChangedProperties]] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.changed"] + occurred_at: str + workspace_id: str - :ivar device_name: + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + change_reason=d.get("change_reason", None), + changed_properties=[ + cls.ChangedProperties.from_dict(i) + for i in d.get("changed_properties") or [] + ], + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) - :ivar minut_metadata: Metadata from Minut. - :ivar noise_level_decibels: Detected noise level in decibels. +@dataclass +class AccessCodeNameChangedEvent: + """The name of an `access code `_ was changed on the device. - :ivar noise_level_nrs: Detected noise level in Noiseaware Noise Risk Score (NRS). + :ivar access_code_id: ID of the affected access code. - :ivar noise_threshold_id: ID of the noise threshold that was triggered. + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. - :ivar noise_threshold_name: Name of the noise threshold that was triggered. + :ivar connected_account_id: ID of the connected account associated with the affected access code. - :ivar noiseaware_metadata: Metadata from Noiseaware. + :ivar created_at: Date and time at which the event was created. - :ivar access_code_is_managed: Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + :ivar description: Human-readable description of the change and its source. - :ivar is_via_bluetooth: + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. - :ivar is_via_nfc: + :ivar device_id: ID of the device associated with the affected access code. - :ivar method: + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. - :ivar reason: Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + :ivar event_id: ID of the event. - :ivar climate_preset_key: Key of the climate preset that was activated. + :ivar event_type: - :ivar is_fallback_climate_preset: Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. + :ivar from_: Previous access code name configuration. - :ivar thermostat_schedule_id: ID of the thermostat schedule that prompted the affected climate preset to be activated. + :ivar occurred_at: Date and time at which the event occurred. - :ivar cooling_set_point_celsius: Temperature to which the thermostat should cool (in °C). See also `Set Points `_. + :ivar to: New access code name configuration. - :ivar cooling_set_point_fahrenheit: Temperature to which the thermostat should cool (in °F). See also `Set Points `_. + :ivar workspace_id: ID of the workspace associated with the event.""" - :ivar fan_mode_setting: Desired `fan mode setting `_, such as ``on``, ``auto``, or ``circulate``. + @dataclass + class From(ResourceMapping): + """Previous access code name configuration. - :ivar heating_set_point_celsius: Temperature to which the thermostat should heat (in °C). See also `Set Points `_. + :ivar name: Previous name of the access code.""" - :ivar heating_set_point_fahrenheit: Temperature to which the thermostat should heat (in °F). See also `Set Points `_. + name: Optional[str] - :ivar hvac_mode_setting: Desired `HVAC mode `_ setting, such as ``heat``, ``cool``, ``heat_cool``, or ``off``. + @classmethod + def from_dict(cls, d: Any): + return cls( + name=d.get("name", None), + ) - :ivar lower_limit_celsius: Lower temperature limit, in °C, defined by the set threshold. + @dataclass + class To(ResourceMapping): + """New access code name configuration. - :ivar lower_limit_fahrenheit: Lower temperature limit, in °F, defined by the set threshold. + :ivar name: New name of the access code.""" - :ivar temperature_celsius: Temperature, in °C, reported by the affected thermostat. + name: Optional[str] - :ivar temperature_fahrenheit: Temperature, in °F, reported by the affected thermostat. + @classmethod + def from_dict(cls, d: Any): + return cls( + name=d.get("name", None), + ) - :ivar upper_limit_celsius: Upper temperature limit, in °C, defined by the set threshold. + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + description: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.name_changed"] + from_: Optional[From] + occurred_at: str + to: Optional[To] + workspace_id: str - :ivar upper_limit_fahrenheit: Upper temperature limit, in °F, defined by the set threshold. + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + description=d.get("description", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + from_=( + cls.From.from_dict(d.get("from")) if d.get("from") is not None else None + ), + occurred_at=d.get("occurred_at", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + workspace_id=d.get("workspace_id", None), + ) - :ivar desired_temperature_celsius: Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. - :ivar desired_temperature_fahrenheit: Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. +@dataclass +class AccessCodeCodeChangedEvent: + """The pin code of an `access code `_ was changed on the device. - :ivar activation_reason: The reason the camera was activated. + :ivar access_code_id: ID of the affected access code. - :ivar image_url: + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. - :ivar motion_sub_type: Sub-type of motion detected, if available. + :ivar connected_account_id: ID of the connected account associated with the affected access code. - :ivar video_url: + :ivar created_at: Date and time at which the event was created. - :ivar acs_entrance_ids: + :ivar description: Human-readable description of the change and its source. - :ivar device_ids: + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. - :ivar space_id: ID of the affected space. + :ivar device_id: ID of the device associated with the affected access code. - :ivar space_key: Unique key for the space within the workspace.""" + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. - @dataclass - class ChangedProperties(ResourceMapping): - """List of properties that changed on the access code. + :ivar event_id: ID of the event. - :ivar from_: Previous value of the property, or null if not set. + :ivar event_type: - :ivar property: Name of the property that changed (e.g. ``code``). + :ivar from_: Previous pin code configuration. - :ivar to: New value of the property, or null if cleared.""" + :ivar occurred_at: Date and time at which the event occurred. - from_: Optional[str] - property: str - to: Optional[str] + :ivar to: New pin code configuration. - @classmethod - def from_dict(cls, d: Any): - return cls( - from_=d.get("from", None), - property=d.get("property", None), - to=d.get("to", None), - ) + :ivar workspace_id: ID of the workspace associated with the event.""" @dataclass class From(ResourceMapping): - """ - - :ivar name: Previous name of the access code. - - :ivar code: Previous pin code. - - :ivar ends_at: Previous end time. + """Previous pin code configuration. - :ivar starts_at: Previous start time.""" + :ivar code: Previous pin code.""" - name: Optional[str] code: Optional[str] - ends_at: Optional[str] - starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - name=d.get("name", None), code=d.get("code", None), - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), ) @dataclass class To(ResourceMapping): - """ + """New pin code configuration. - :ivar name: New name of the access code. + :ivar code: New pin code.""" - :ivar code: New pin code. - - :ivar ends_at: New end time. - - :ivar starts_at: New start time.""" - - name: Optional[str] code: Optional[str] - ends_at: Optional[str] - starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - name=d.get("name", None), code=d.get("code", None), - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), ) - @dataclass - class RequestedMutations(ResourceMapping): - """Array of mutations requested on the access code, each containing the mutation type and from/to values. + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + description: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.code_changed"] + from_: Optional[From] + occurred_at: str + to: Optional[To] + workspace_id: str - :ivar from_: Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like ``deleting``. + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + description=d.get("description", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + from_=( + cls.From.from_dict(d.get("from")) if d.get("from") is not None else None + ), + occurred_at=d.get("occurred_at", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + workspace_id=d.get("workspace_id", None), + ) - :ivar mutation_code: Code identifying the type of mutation requested, such as ``updating_name``, ``updating_code``, ``updating_time_frame``, or ``deleting``. - :ivar to: New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like ``deleting``. - """ +@dataclass +class AccessCodeTimeFrameChangedEvent: + """The time frame of an `access code `_ was changed on the device. - from_: Optional[Dict[str, Any]] - mutation_code: str - to: Optional[Dict[str, Any]] + :ivar access_code_id: ID of the affected access code. - @classmethod - def from_dict(cls, d: Any): - return cls( - from_=DeepAttrDict(d.get("from", None)), - mutation_code=d.get("mutation_code", None), - to=DeepAttrDict(d.get("to", None)), - ) + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. - @dataclass - class AccessCodeErrors(ResourceMapping): - """Errors associated with the access code. + :ivar connected_account_id: ID of the connected account associated with the affected access code. - :ivar created_at: Date and time at which Seam created the error. + :ivar created_at: Date and time at which the event was created. - :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + :ivar description: Human-readable description of the change and its source. - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - """ + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. - created_at: str - error_code: str - message: str + :ivar device_id: ID of the device associated with the affected access code. - @classmethod - def from_dict(cls, d: Any): - return cls( - created_at=d.get("created_at", None), - error_code=d.get("error_code", None), - message=d.get("message", None), - ) + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. - @dataclass - class AccessCodeWarnings(ResourceMapping): - """Warnings associated with the access code. + :ivar event_id: ID of the event. - :ivar created_at: Date and time at which Seam created the warning. + :ivar event_type: - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar from_: Previous time frame configuration. - :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - """ + :ivar occurred_at: Date and time at which the event occurred. - created_at: str - message: str - warning_code: str + :ivar to: New time frame configuration. - @classmethod - def from_dict(cls, d: Any): - return cls( - created_at=d.get("created_at", None), - message=d.get("message", None), - warning_code=d.get("warning_code", None), - ) + :ivar workspace_id: ID of the workspace associated with the event.""" @dataclass - class ConnectedAccountErrors(ResourceMapping): - """Errors associated with the connected account. - - :ivar created_at: Date and time at which Seam created the error. + class From(ResourceMapping): + """Previous time frame configuration. - :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + :ivar ends_at: Previous end time. - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - """ + :ivar starts_at: Previous start time.""" - created_at: str - error_code: str - message: str + ends_at: Optional[str] + starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - created_at=d.get("created_at", None), - error_code=d.get("error_code", None), - message=d.get("message", None), + ends_at=d.get("ends_at", None), + starts_at=d.get("starts_at", None), ) @dataclass - class ConnectedAccountWarnings(ResourceMapping): - """Warnings associated with the connected account. - - :ivar created_at: Date and time at which Seam created the warning. + class To(ResourceMapping): + """New time frame configuration. - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar ends_at: New end time. - :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - """ + :ivar starts_at: New start time.""" - created_at: str - message: str - warning_code: str + ends_at: Optional[str] + starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - created_at=d.get("created_at", None), - message=d.get("message", None), - warning_code=d.get("warning_code", None), + ends_at=d.get("ends_at", None), + starts_at=d.get("starts_at", None), ) - @dataclass - class DeviceErrors(ResourceMapping): - """Errors associated with the device. - - :ivar created_at: Date and time at which Seam created the error. - - :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + description: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.time_frame_changed"] + from_: Optional[From] + occurred_at: str + to: Optional[To] + workspace_id: str - :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): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + description=d.get("description", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + from_=( + cls.From.from_dict(d.get("from")) if d.get("from") is not None else None + ), + occurred_at=d.get("occurred_at", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + workspace_id=d.get("workspace_id", None), + ) - created_at: str - error_code: str - message: str - @classmethod - def from_dict(cls, d: Any): - return cls( - created_at=d.get("created_at", None), - error_code=d.get("error_code", None), - message=d.get("message", None), - ) +@dataclass +class AccessCodeMutationsRequestedEvent: + """Mutations were requested on an `access code `_. This event fires at request time, before the change is confirmed on the device. - @dataclass - class DeviceWarnings(ResourceMapping): - """Warnings associated with the device. + :ivar access_code_id: ID of the affected access code. - :ivar created_at: Date and time at which Seam created the warning. + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar connected_account_id: ID of the connected account associated with the affected access code. - :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. - """ + :ivar created_at: Date and time at which the event was created. - created_at: str - message: str - warning_code: str + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. - @classmethod - def from_dict(cls, d: Any): - return cls( - created_at=d.get("created_at", None), - message=d.get("message", None), - warning_code=d.get("warning_code", None), - ) + :ivar device_id: ID of the device associated with the affected access code. - @dataclass - class AcsSystemErrors(ResourceMapping): - """Errors associated with the access control system. + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. - :ivar created_at: Date and time at which Seam created the error. + :ivar event_id: ID of the event. - :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + :ivar event_type: - :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - """ + :ivar occurred_at: Date and time at which the event occurred. - created_at: str - error_code: str - message: str + :ivar requested_mutations: Array of mutations requested on the access code, each containing the mutation type and from/to values. - @classmethod - def from_dict(cls, d: Any): - return cls( - created_at=d.get("created_at", None), - error_code=d.get("error_code", None), - message=d.get("message", None), - ) + :ivar workspace_id: ID of the workspace associated with the event.""" @dataclass - class AcsSystemWarnings(ResourceMapping): - """Warnings associated with the access control system. + class RequestedMutations(ResourceMapping): + """Array of mutations requested on the access code, each containing the mutation type and from/to values. - :ivar created_at: Date and time at which Seam created the warning. + :ivar from_: Previous property values before the requested change. Keys depend on the mutation type. Absent for non-property mutations like ``deleting``. - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar mutation_code: Code identifying the type of mutation requested, such as ``updating_name``, ``updating_code``, ``updating_time_frame``, or ``deleting``. - :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + :ivar to: New property values after the requested change. Keys depend on the mutation type. Absent for non-property mutations like ``deleting``. """ - created_at: str - message: str - warning_code: str + from_: Optional[Dict[str, Any]] + mutation_code: Literal[ + "updating_name", + "updating_code", + "updating_time_frame", + "deleting", + "creating", + "deferring_creation", + ] + to: Optional[Dict[str, Any]] @classmethod def from_dict(cls, d: Any): return cls( - created_at=d.get("created_at", None), - message=d.get("message", None), - warning_code=d.get("warning_code", None), + from_=DeepAttrDict(d.get("from", None)), + mutation_code=d.get("mutation_code", None), + to=DeepAttrDict(d.get("to", None)), ) - @dataclass - class Reason(ResourceMapping): - """Why access was denied, when the provider reports a determinable cause. Omitted when unknown. - - :ivar message: Human-readable explanation of why access was denied. - - :ivar reason_code: Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. - """ + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.mutations_requested"] + occurred_at: str + requested_mutations: List[RequestedMutations] + workspace_id: str - message: str - reason_code: str + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + requested_mutations=[ + cls.RequestedMutations.from_dict(i) + for i in d.get("requested_mutations") or [] + ], + workspace_id=d.get("workspace_id", None), + ) - @classmethod - def from_dict(cls, d: Any): - return cls( - message=d.get("message", None), - reason_code=d.get("reason_code", None), - ) - access_code_id: Optional[str] +@dataclass +class AccessCodeScheduledOnDeviceEvent: + """An `access code `_ was `scheduled natively `_ on a device. + + :ivar access_code_id: ID of the affected access code. + + :ivar code: Code for the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + code: str connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] - connected_account_id: Optional[str] + connected_account_id: str created_at: str device_custom_metadata: Optional[Dict[str, Union[str, bool]]] - device_id: Optional[str] + device_id: str event_description: Optional[str] event_id: str - event_type: str + event_type: Literal["access_code.scheduled_on_device"] occurred_at: str workspace_id: str - change_reason: Optional[str] - changed_properties: Optional[List[ChangedProperties]] - description: Optional[str] - from_: Optional[From] - to: Optional[To] - requested_mutations: Optional[List[RequestedMutations]] - code: Optional[str] - access_code_errors: Optional[List[AccessCodeErrors]] - access_code_warnings: Optional[List[AccessCodeWarnings]] - connected_account_errors: Optional[List[ConnectedAccountErrors]] - connected_account_warnings: Optional[List[ConnectedAccountWarnings]] - device_errors: Optional[List[DeviceErrors]] - device_warnings: Optional[List[DeviceWarnings]] - backup_access_code_id: Optional[str] - access_grant_id: Optional[str] - acs_entrance_id: Optional[str] - access_grant_key: Optional[str] - ends_at: Optional[str] - starts_at: Optional[str] - error_message: Optional[str] - missing_device_ids: Optional[List[str]] - access_grant_ids: Optional[List[str]] - access_grant_keys: Optional[List[str]] - access_method_id: Optional[str] - is_backup_code: Optional[bool] - acs_system_id: Optional[str] - acs_system_errors: Optional[List[AcsSystemErrors]] - acs_system_warnings: Optional[List[AcsSystemWarnings]] - acs_credential_id: Optional[str] - acs_user_id: Optional[str] - acs_encoder_id: Optional[str] - acs_access_group_id: Optional[str] - client_session_id: Optional[str] - connect_webview_id: Optional[str] - customer_key: Optional[str] - action_attempt_id: Optional[str] - action_type: Optional[str] - status: Optional[str] - error_code: Optional[str] - battery_level: Optional[float] - battery_status: Optional[str] - device_name: Optional[str] - minut_metadata: Optional[Dict[str, Any]] - noise_level_decibels: Optional[float] - noise_level_nrs: Optional[float] - noise_threshold_id: Optional[str] - noise_threshold_name: Optional[str] - noiseaware_metadata: Optional[Dict[str, Any]] - access_code_is_managed: Optional[bool] - is_via_bluetooth: Optional[bool] - is_via_nfc: Optional[bool] - method: Optional[str] - reason: Optional[Reason] - climate_preset_key: Optional[str] - is_fallback_climate_preset: Optional[bool] - thermostat_schedule_id: Optional[str] - cooling_set_point_celsius: Optional[float] - cooling_set_point_fahrenheit: Optional[float] - fan_mode_setting: Optional[str] - heating_set_point_celsius: Optional[float] - heating_set_point_fahrenheit: Optional[float] - hvac_mode_setting: Optional[str] - lower_limit_celsius: Optional[float] - lower_limit_fahrenheit: Optional[float] - temperature_celsius: Optional[float] - temperature_fahrenheit: Optional[float] - upper_limit_celsius: Optional[float] - upper_limit_fahrenheit: Optional[float] - desired_temperature_celsius: Optional[float] - desired_temperature_fahrenheit: Optional[float] - activation_reason: Optional[str] - image_url: Optional[str] - motion_sub_type: Optional[str] - video_url: Optional[str] - acs_entrance_ids: Optional[List[str]] - device_ids: Optional[List[str]] - space_id: Optional[str] - space_key: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( access_code_id=d.get("access_code_id", None), + code=d.get("code", None), connected_account_custom_metadata=DeepAttrDict( d.get("connected_account_custom_metadata", None) ), @@ -592,118 +608,7541 @@ def from_dict(cls, d: Any): event_type=d.get("event_type", None), occurred_at=d.get("occurred_at", None), workspace_id=d.get("workspace_id", None), - change_reason=d.get("change_reason", None), - changed_properties=[ - cls.ChangedProperties.from_dict(i) - for i in d.get("changed_properties") or [] - ], - description=d.get("description", None), - from_=( - cls.From.from_dict(d.get("from")) if d.get("from") is not None else None - ), - to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, - requested_mutations=[ - cls.RequestedMutations.from_dict(i) - for i in d.get("requested_mutations") or [] - ], + ) + + +@dataclass +class AccessCodeSetOnDeviceEvent: + """An `access code `_ was set on a device. + + :ivar access_code_id: ID of the affected access code. + + :ivar code: Code for the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + code: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.set_on_device"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), code=d.get("code", None), - access_code_errors=[ - cls.AccessCodeErrors.from_dict(i) - for i in d.get("access_code_errors") or [] - ], - access_code_warnings=[ - cls.AccessCodeWarnings.from_dict(i) - for i in d.get("access_code_warnings") or [] - ], - connected_account_errors=[ - cls.ConnectedAccountErrors.from_dict(i) - for i in d.get("connected_account_errors") or [] - ], - connected_account_warnings=[ - cls.ConnectedAccountWarnings.from_dict(i) - for i in d.get("connected_account_warnings") or [] - ], - device_errors=[ - cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] - ], - device_warnings=[ - cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] - ], - backup_access_code_id=d.get("backup_access_code_id", None), - access_grant_id=d.get("access_grant_id", None), - acs_entrance_id=d.get("acs_entrance_id", None), - access_grant_key=d.get("access_grant_key", None), - ends_at=d.get("ends_at", None), - starts_at=d.get("starts_at", None), - error_message=d.get("error_message", None), - missing_device_ids=d.get("missing_device_ids", None), - access_grant_ids=d.get("access_grant_ids", None), - access_grant_keys=d.get("access_grant_keys", None), - access_method_id=d.get("access_method_id", None), - is_backup_code=d.get("is_backup_code", None), - acs_system_id=d.get("acs_system_id", None), - acs_system_errors=[ - cls.AcsSystemErrors.from_dict(i) - for i in d.get("acs_system_errors") or [] - ], - acs_system_warnings=[ - cls.AcsSystemWarnings.from_dict(i) - for i in d.get("acs_system_warnings") or [] - ], - acs_credential_id=d.get("acs_credential_id", None), - acs_user_id=d.get("acs_user_id", None), - acs_encoder_id=d.get("acs_encoder_id", None), - acs_access_group_id=d.get("acs_access_group_id", None), - client_session_id=d.get("client_session_id", None), - connect_webview_id=d.get("connect_webview_id", None), - customer_key=d.get("customer_key", None), - action_attempt_id=d.get("action_attempt_id", None), - action_type=d.get("action_type", None), - status=d.get("status", None), - error_code=d.get("error_code", None), - battery_level=d.get("battery_level", None), - battery_status=d.get("battery_status", None), - device_name=d.get("device_name", None), - minut_metadata=DeepAttrDict(d.get("minut_metadata", None)), - noise_level_decibels=d.get("noise_level_decibels", None), - noise_level_nrs=d.get("noise_level_nrs", None), - noise_threshold_id=d.get("noise_threshold_id", None), - noise_threshold_name=d.get("noise_threshold_name", None), - noiseaware_metadata=DeepAttrDict(d.get("noiseaware_metadata", None)), - access_code_is_managed=d.get("access_code_is_managed", None), - is_via_bluetooth=d.get("is_via_bluetooth", None), - is_via_nfc=d.get("is_via_nfc", None), - method=d.get("method", None), - reason=( - cls.Reason.from_dict(d.get("reason")) - if d.get("reason") is not None - else None - ), - climate_preset_key=d.get("climate_preset_key", None), - is_fallback_climate_preset=d.get("is_fallback_climate_preset", None), - thermostat_schedule_id=d.get("thermostat_schedule_id", None), - cooling_set_point_celsius=d.get("cooling_set_point_celsius", None), - cooling_set_point_fahrenheit=d.get("cooling_set_point_fahrenheit", None), - fan_mode_setting=d.get("fan_mode_setting", None), - heating_set_point_celsius=d.get("heating_set_point_celsius", None), - heating_set_point_fahrenheit=d.get("heating_set_point_fahrenheit", None), - hvac_mode_setting=d.get("hvac_mode_setting", None), - lower_limit_celsius=d.get("lower_limit_celsius", None), - lower_limit_fahrenheit=d.get("lower_limit_fahrenheit", None), - temperature_celsius=d.get("temperature_celsius", None), - temperature_fahrenheit=d.get("temperature_fahrenheit", None), - upper_limit_celsius=d.get("upper_limit_celsius", None), - upper_limit_fahrenheit=d.get("upper_limit_fahrenheit", None), - desired_temperature_celsius=d.get("desired_temperature_celsius", None), - desired_temperature_fahrenheit=d.get( - "desired_temperature_fahrenheit", None + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) ), - activation_reason=d.get("activation_reason", None), - image_url=d.get("image_url", None), - motion_sub_type=d.get("motion_sub_type", None), - video_url=d.get("video_url", None), - acs_entrance_ids=d.get("acs_entrance_ids", None), - device_ids=d.get("device_ids", None), - space_id=d.get("space_id", None), - space_key=d.get("space_key", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), ) + + +@dataclass +class AccessCodeRemovedFromDeviceEvent: + """An `access code `_ was removed from a device. + + :ivar access_code_id: ID of the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.removed_from_device"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeDelayInSettingOnDeviceEvent: + """There was an unusually long delay in setting an `access code `_ on a device. + + :ivar access_code_errors: Errors associated with the access code. + + :ivar access_code_id: ID of the affected access code. + + :ivar access_code_warnings: Warnings associated with the access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class AccessCodeErrors(ResourceMapping): + """Errors associated with the access code. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AccessCodeWarnings(ResourceMapping): + """Warnings associated with the access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + access_code_errors: List[AccessCodeErrors] + access_code_id: str + access_code_warnings: List[AccessCodeWarnings] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.delay_in_setting_on_device"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_errors=[ + cls.AccessCodeErrors.from_dict(i) + for i in d.get("access_code_errors") or [] + ], + access_code_id=d.get("access_code_id", None), + access_code_warnings=[ + cls.AccessCodeWarnings.from_dict(i) + for i in d.get("access_code_warnings") or [] + ], + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeFailedToSetOnDeviceEvent: + """An `access code `_ failed to be set on a device. + + :ivar access_code_errors: Errors associated with the access code. + + :ivar access_code_id: ID of the affected access code. + + :ivar access_code_warnings: Warnings associated with the access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class AccessCodeErrors(ResourceMapping): + """Errors associated with the access code. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AccessCodeWarnings(ResourceMapping): + """Warnings associated with the access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + access_code_errors: List[AccessCodeErrors] + access_code_id: str + access_code_warnings: List[AccessCodeWarnings] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.failed_to_set_on_device"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_errors=[ + cls.AccessCodeErrors.from_dict(i) + for i in d.get("access_code_errors") or [] + ], + access_code_id=d.get("access_code_id", None), + access_code_warnings=[ + cls.AccessCodeWarnings.from_dict(i) + for i in d.get("access_code_warnings") or [] + ], + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeDeletedEvent: + """An `access code `_ was deleted. + + :ivar access_code_id: ID of the affected access code. + + :ivar code: Code for the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + code: Optional[str] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + code=d.get("code", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeDelayInRemovingFromDeviceEvent: + """There was an unusually long delay in removing an `access code `_ from a device. + + :ivar access_code_errors: Errors associated with the access code. + + :ivar access_code_id: ID of the affected access code. + + :ivar access_code_warnings: Warnings associated with the access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class AccessCodeErrors(ResourceMapping): + """Errors associated with the access code. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AccessCodeWarnings(ResourceMapping): + """Warnings associated with the access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + access_code_errors: List[AccessCodeErrors] + access_code_id: str + access_code_warnings: List[AccessCodeWarnings] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.delay_in_removing_from_device"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_errors=[ + cls.AccessCodeErrors.from_dict(i) + for i in d.get("access_code_errors") or [] + ], + access_code_id=d.get("access_code_id", None), + access_code_warnings=[ + cls.AccessCodeWarnings.from_dict(i) + for i in d.get("access_code_warnings") or [] + ], + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeFailedToRemoveFromDeviceEvent: + """An `access code `_ failed to be removed from a device. + + :ivar access_code_errors: Errors associated with the access code. + + :ivar access_code_id: ID of the affected access code. + + :ivar access_code_warnings: Warnings associated with the access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class AccessCodeErrors(ResourceMapping): + """Errors associated with the access code. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AccessCodeWarnings(ResourceMapping): + """Warnings associated with the access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + access_code_errors: List[AccessCodeErrors] + access_code_id: str + access_code_warnings: List[AccessCodeWarnings] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.failed_to_remove_from_device"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_errors=[ + cls.AccessCodeErrors.from_dict(i) + for i in d.get("access_code_errors") or [] + ], + access_code_id=d.get("access_code_id", None), + access_code_warnings=[ + cls.AccessCodeWarnings.from_dict(i) + for i in d.get("access_code_warnings") or [] + ], + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeModifiedExternalToSeamEvent: + """An `access code `_ was modified outside of Seam. + + :ivar access_code_id: ID of the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.modified_external_to_seam"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeDeletedExternalToSeamEvent: + """An `access code `_ was deleted outside of Seam. + + :ivar access_code_id: ID of the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.deleted_external_to_seam"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeBackupAccessCodePulledEvent: + """A `backup access code `_ was pulled from the backup access code pool and set on a device. + + :ivar access_code_id: ID of the affected access code. + + :ivar backup_access_code_id: ID of the backup access code that was pulled from the pool. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + backup_access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.backup_access_code_pulled"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + backup_access_code_id=d.get("backup_access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeUnmanagedConvertedToManagedEvent: + """An `unmanaged access code `_ was converted successfully to a managed access code. + + :ivar access_code_id: ID of the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.unmanaged.converted_to_managed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeUnmanagedFailedToConvertToManagedEvent: + """An `unmanaged access code `_ failed to be converted to a managed access code. + + :ivar access_code_errors: Errors associated with the access code. + + :ivar access_code_id: ID of the affected access code. + + :ivar access_code_warnings: Warnings associated with the access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class AccessCodeErrors(ResourceMapping): + """Errors associated with the access code. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AccessCodeWarnings(ResourceMapping): + """Warnings associated with the access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + access_code_errors: List[AccessCodeErrors] + access_code_id: str + access_code_warnings: List[AccessCodeWarnings] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.unmanaged.failed_to_convert_to_managed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_errors=[ + cls.AccessCodeErrors.from_dict(i) + for i in d.get("access_code_errors") or [] + ], + access_code_id=d.get("access_code_id", None), + access_code_warnings=[ + cls.AccessCodeWarnings.from_dict(i) + for i in d.get("access_code_warnings") or [] + ], + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeUnmanagedCreatedEvent: + """An `unmanaged access code `_ was created on a device. + + :ivar access_code_id: ID of the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.unmanaged.created"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessCodeUnmanagedRemovedEvent: + """An `unmanaged access code `_ was removed from a device. + + :ivar access_code_id: ID of the affected access code. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the affected access code. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the device associated with the affected access code. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_code.unmanaged.removed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessGrantCreatedEvent: + """An Access Grant was created. + + :ivar access_grant_id: ID of the affected Access Grant. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_grant.created"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_id=d.get("access_grant_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessGrantDeletedEvent: + """An Access Grant was deleted. + + :ivar access_grant_id: ID of the affected Access Grant. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_grant.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_id=d.get("access_grant_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessGrantAccessGrantedToAllDoorsEvent: + """All access requested for an Access Grant was successfully granted. + + :ivar access_grant_id: ID of the affected Access Grant. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_grant.access_granted_to_all_doors"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_id=d.get("access_grant_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessGrantAccessGrantedToDoorEvent: + """Access requested as part of an Access Grant to a particular door was successfully granted. + + :ivar access_grant_id: ID of the affected Access Grant. + + :ivar acs_entrance_id: ID of the affected `entrance `_. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_id: str + acs_entrance_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_grant.access_granted_to_door"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_id=d.get("access_grant_id", None), + acs_entrance_id=d.get("acs_entrance_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessGrantAccessToDoorLostEvent: + """Access to a particular door that was requested as part of an Access Grant was lost. + + :ivar access_grant_id: ID of the affected Access Grant. + + :ivar acs_entrance_id: ID of the affected `entrance `_. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_id: str + acs_entrance_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_grant.access_to_door_lost"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_id=d.get("access_grant_id", None), + acs_entrance_id=d.get("acs_entrance_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessGrantAccessTimesChangedEvent: + """An Access Grant's start or end time was changed. + + :ivar access_grant_id: ID of the affected Access Grant. + + :ivar access_grant_key: Key of the affected Access Grant (if present). + + :ivar created_at: Date and time at which the event was created. + + :ivar ends_at: The new end time for the access grant. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar starts_at: The new start time for the access grant. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_id: str + access_grant_key: Optional[str] + created_at: str + ends_at: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["access_grant.access_times_changed"] + occurred_at: str + starts_at: Optional[str] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_id=d.get("access_grant_id", None), + access_grant_key=d.get("access_grant_key", None), + created_at=d.get("created_at", None), + ends_at=d.get("ends_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + starts_at=d.get("starts_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessGrantCouldNotCreateRequestedAccessMethodsEvent: + """One or more requested access methods could not be created for an Access Grant. + + :ivar access_grant_id: ID of the affected Access Grant. + + :ivar created_at: Date and time at which the event was created. + + :ivar error_message: Description of why the access methods could not be created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar missing_device_ids: IDs of the devices that did not receive a requested access method. Use these to identify which specific devices failed without having to fetch the Access Grant. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_id: str + created_at: str + error_message: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_grant.could_not_create_requested_access_methods"] + missing_device_ids: Optional[List[str]] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_id=d.get("access_grant_id", None), + created_at=d.get("created_at", None), + error_message=d.get("error_message", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + missing_device_ids=d.get("missing_device_ids", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodIssuedEvent: + """An access method was issued. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar code: The actual PIN code for code access methods (only present when mode is 'code'). + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar is_backup_code: Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + code: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.issued"] + is_backup_code: Optional[bool] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + code=d.get("code", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + is_backup_code=d.get("is_backup_code", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodRevokedEvent: + """An access method was revoked. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.revoked"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodCardEncodingRequiredEvent: + """An access method representing a physical card requires encoding. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.card_encoding_required"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodDeletedEvent: + """An access method was deleted. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodReissuedEvent: + """An access method was reissued. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar code: The actual PIN code for code access methods (only present when mode is 'code'). + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar is_backup_code: Indicates whether the code is a backup code (only present when mode is 'code' and a backup code was used). + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + code: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.reissued"] + is_backup_code: Optional[bool] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + code=d.get("code", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + is_backup_code=d.get("is_backup_code", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodCreatedEvent: + """An access method was created. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.created"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodDelayInIssuingEvent: + """Seam has not yet issued this access method, even though its access grant is about to begin, so access may not be ready when the recipient arrives. Seam is still attempting to issue it, and the accompanying ``delay_in_issuing`` warning clears automatically once issuance succeeds. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.delay_in_issuing"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AccessMethodFailedToIssueEvent: + """Seam was unable to issue this access method before its access grant started, so the recipient may be unable to access the space. This usually points to a problem that needs attention, such as an offline or disconnected device. Seam keeps retrying, and the accompanying ``failed_to_issue`` error clears automatically if the access method is eventually issued. + + :ivar access_grant_ids: IDs of the access grants associated with this access method. + + :ivar access_grant_keys: Keys of the access grants associated with this access method (if present). + + :ivar access_method_id: ID of the affected access method. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_grant_ids: List[str] + access_grant_keys: Optional[List[str]] + access_method_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["access_method.failed_to_issue"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_grant_ids=d.get("access_grant_ids", None), + access_grant_keys=d.get("access_grant_keys", None), + access_method_id=d.get("access_method_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsSystemConnectedEvent: + """An `access system `_ was connected. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_system.connected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsSystemAddedEvent: + """An `access system `_ was added. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_system.added"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsSystemDisconnectedEvent: + """An `access system `_ was disconnected. + + :ivar acs_system_errors: Errors associated with the access control system. + + :ivar acs_system_id: ID of the access system. + + :ivar acs_system_warnings: Warnings associated with the access control system. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class AcsSystemErrors(ResourceMapping): + """Errors associated with the access control system. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class AcsSystemWarnings(ResourceMapping): + """Warnings associated with the access control system. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + acs_system_errors: List[AcsSystemErrors] + acs_system_id: str + acs_system_warnings: List[AcsSystemWarnings] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: Optional[str] + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_system.disconnected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_system_errors=[ + cls.AcsSystemErrors.from_dict(i) + for i in d.get("acs_system_errors") or [] + ], + acs_system_id=d.get("acs_system_id", None), + acs_system_warnings=[ + cls.AcsSystemWarnings.from_dict(i) + for i in d.get("acs_system_warnings") or [] + ], + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsCredentialDeletedEvent: + """An `access system credential `_ was deleted. + + :ivar acs_credential_id: ID of the affected credential. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_credential_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_credential.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_credential_id=d.get("acs_credential_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsCredentialIssuedEvent: + """An `access system credential `_ was issued. + + :ivar acs_credential_id: ID of the affected credential. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_credential_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_credential.issued"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_credential_id=d.get("acs_credential_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsCredentialReissuedEvent: + """An `access system credential `_ was reissued. + + :ivar acs_credential_id: ID of the affected credential. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_credential_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_credential.reissued"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_credential_id=d.get("acs_credential_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsCredentialInvalidatedEvent: + """An `access system credential `_ was invalidated. That is, the credential cannot be used anymore. + + :ivar acs_credential_id: ID of the affected credential. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_credential_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_credential.invalidated"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_credential_id=d.get("acs_credential_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsUserCreatedEvent: + """An `access system user `_ was created. + + :ivar acs_system_id: ID of the access system. + + :ivar acs_user_id: ID of the affected access system user. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_system_id: str + acs_user_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_user.created"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_system_id=d.get("acs_system_id", None), + acs_user_id=d.get("acs_user_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsUserDeletedEvent: + """An `access system user `_ was deleted. + + :ivar acs_system_id: ID of the access system. + + :ivar acs_user_id: ID of the affected access system user. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_system_id: str + acs_user_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_user.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_system_id=d.get("acs_system_id", None), + acs_user_id=d.get("acs_user_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsEncoderAddedEvent: + """An `access system encoder `_ was added. + + :ivar acs_encoder_id: ID of the affected encoder. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_encoder_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_encoder.added"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_encoder_id=d.get("acs_encoder_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsEncoderRemovedEvent: + """An `access system encoder `_ was removed. + + :ivar acs_encoder_id: ID of the affected encoder. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_encoder_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_encoder.removed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_encoder_id=d.get("acs_encoder_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsAccessGroupDeletedEvent: + """An ACS access group was deleted. + + :ivar acs_access_group_id: ID of the affected access group. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_access_group_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_access_group.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_access_group_id=d.get("acs_access_group_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsEntranceAddedEvent: + """An `access system entrance `_ was added. + + :ivar acs_entrance_id: ID of the affected entrance. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_entrance_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_entrance.added"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_entrance_id=d.get("acs_entrance_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class AcsEntranceRemovedEvent: + """An `access system entrance `_ was removed. + + :ivar acs_entrance_id: ID of the affected entrance. + + :ivar acs_system_id: ID of the access system. + + :ivar connected_account_id: ID of the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_entrance_id: str + acs_system_id: str + connected_account_id: Optional[str] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["acs_entrance.removed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_entrance_id=d.get("acs_entrance_id", None), + acs_system_id=d.get("acs_system_id", None), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ClientSessionDeletedEvent: + """A client session was deleted. + + :ivar client_session_id: ID of the affected client session. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + client_session_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["client_session.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + client_session_id=d.get("client_session_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountConnectedEvent: + """A connected account was connected for the first time or was reconnected after being disconnected. + + :ivar connect_webview_id: ID of the Connect Webview associated with the event. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with this connected account, if any. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connect_webview_id: Optional[str] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.connected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connect_webview_id=d.get("connect_webview_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountCreatedEvent: + """A connected account was created. + + :ivar connect_webview_id: ID of the Connect Webview associated with the event. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connect_webview_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.created"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connect_webview_id=d.get("connect_webview_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountSuccessfulLoginEvent: + """A connected account had a successful login using a Connect Webview. + + :ivar connect_webview_id: ID of the Connect Webview associated with the event. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event. + + .. deprecated:: + Use ``connect_webview.login_succeeded``.""" + + connect_webview_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.successful_login"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connect_webview_id=d.get("connect_webview_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountDisconnectedEvent: + """A connected account was disconnected. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.disconnected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountCompletedFirstSyncEvent: + """A connected account completed the first sync with Seam, and the corresponding devices or systems are now available. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.completed_first_sync"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountDeletedEvent: + """A connected account was deleted. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with this connected account, if any. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountCompletedFirstSyncAfterReconnectionEvent: + """A connected account completed the first sync after reconnection with Seam, and the corresponding devices or systems are now available. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.completed_first_sync_after_reconnection"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectedAccountReauthorizationRequestedEvent: + """A connected account requires reauthorization using a new Connect Webview. The account is still connected, but cannot access new features. Delaying reauthorization too long will eventually cause the Connected Account to become disconnected. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the affected connected account. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["connected_account.reauthorization_requested"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptLockDoorSucceededEvent: + """A lock door action attempt succeeded. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.lock_door.succeeded"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptLockDoorFailedEvent: + """A lock door action attempt failed. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.lock_door.failed"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptUnlockDoorSucceededEvent: + """An unlock door action attempt succeeded. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.unlock_door.succeeded"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptUnlockDoorFailedEvent: + """An unlock door action attempt failed. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.unlock_door.failed"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptSimulateKeypadCodeEntrySucceededEvent: + """A simulate keypad code entry action attempt succeeded. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.simulate_keypad_code_entry.succeeded"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptSimulateKeypadCodeEntryFailedEvent: + """A simulate keypad code entry action attempt failed. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.simulate_keypad_code_entry.failed"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptSimulateManualLockViaKeypadSucceededEvent: + """A simulate manual lock via keypad action attempt succeeded. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.simulate_manual_lock_via_keypad.succeeded"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ActionAttemptSimulateManualLockViaKeypadFailedEvent: + """A simulate manual lock via keypad action attempt failed. + + :ivar action_attempt_id: ID of the affected action attempt. + + :ivar action_type: Type of the action. + + :ivar connected_account_id: ID of the connected account associated with the action attempt, if applicable. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_id: ID of the device associated with the action attempt, if applicable. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar status: Status of the action. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + action_attempt_id: str + action_type: str + connected_account_id: Optional[str] + created_at: str + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["action_attempt.simulate_manual_lock_via_keypad.failed"] + occurred_at: str + status: str + workspace_id: 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), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + status=d.get("status", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectWebviewLoginSucceededEvent: + """A Connect Webview login succeeded. + + :ivar connect_webview_id: ID of the affected Connect Webview. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account; present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with this connect webview, if any. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connect_webview_id: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["connect_webview.login_succeeded"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connect_webview_id=d.get("connect_webview_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ConnectWebviewLoginFailedEvent: + """A Connect Webview login failed. + + :ivar connect_webview_id: ID of the affected Connect Webview. + + :ivar created_at: Date and time at which the event was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connect_webview_id: str + created_at: str + event_description: Optional[str] + event_id: str + event_type: Literal["connect_webview.login_failed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connect_webview_id=d.get("connect_webview_id", None), + created_at=d.get("created_at", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceConnectedEvent: + """The status of a device changed from offline to online. That is, the ``device.properties.online`` property changed from ``false`` to ``true``. Note that some devices operate entirely in offline mode, so Seam never emits a ``device.connected`` event for these devices. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.connected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceAddedEvent: + """A device was added to Seam or was re-added to Seam after having been removed. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.added"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceConvertedToUnmanagedEvent: + """A managed device was successfully converted to an `unmanaged device `_. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.converted_to_unmanaged"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceUnmanagedConvertedToManagedEvent: + """An `unmanaged device `_ was successfully converted to a managed device. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.unmanaged.converted_to_managed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceUnmanagedConnectedEvent: + """The status of an `unmanaged device `_ changed from offline to online. That is, the ``device.properties.online`` property changed from ``false`` to ``true``. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.unmanaged.connected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceDisconnectedEvent: + """The status of a device changed from online to offline. That is, the ``device.properties.online`` property changed from ``true`` to ``false``. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the affected device. + + :ivar device_warnings: Warnings associated with the device. + + :ivar error_code: Error code associated with the disconnection event, if any. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + error_code: Literal[ + "account_disconnected", "hub_disconnected", "device_disconnected" + ] + event_description: Optional[str] + event_id: str + event_type: Literal["device.disconnected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + error_code=d.get("error_code", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceUnmanagedDisconnectedEvent: + """The status of an `unmanaged device `_ changed from online to offline. That is, the ``device.properties.online`` property changed from ``true`` to ``false``. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the affected device. + + :ivar device_warnings: Warnings associated with the device. + + :ivar error_code: Error code associated with the disconnection event, if any. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + error_code: Literal[ + "account_disconnected", "hub_disconnected", "device_disconnected" + ] + event_description: Optional[str] + event_id: str + event_type: Literal["device.unmanaged.disconnected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + error_code=d.get("error_code", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceTamperedEvent: + """A device detected that it was tampered with, for example, opened or moved. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.tampered"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceLowBatteryEvent: + """A device battery level dropped below the low threshold. + + :ivar battery_level: Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + battery_level: float + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.low_battery"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + battery_level=d.get("battery_level", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceBatteryStatusChangedEvent: + """A device battery status changed since the last ``battery_status_changed`` event. + + :ivar battery_level: Number in the range 0 to 1.0 indicating the amount of battery in the affected device, as reported by the device. + + :ivar battery_status: Battery status of the affected device, calculated from the numeric ``battery_level`` value. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + battery_level: float + battery_status: Literal["critical", "low", "good", "full"] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.battery_status_changed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + battery_level=d.get("battery_level", None), + battery_status=d.get("battery_status", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceRemovedEvent: + """A device was removed externally from the connected account. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.removed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceDeletedEvent: + """A device was deleted. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar device_name: Name of the deleted device, captured at deletion time. The device record no longer exists when this event fires, so the name is preserved here. Null when the device had no resolvable name. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + device_name: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["device.deleted"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + device_name=d.get("device_name", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceThirdPartyIntegrationDetectedEvent: + """Seam detected that a device is using a third-party integration that will interfere with Seam device management. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.third_party_integration_detected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceThirdPartyIntegrationNoLongerDetectedEvent: + """Seam detected that a device is no longer using a third-party integration that was interfering with Seam device management. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.third_party_integration_no_longer_detected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceSaltoPrivacyModeActivatedEvent: + """A `Salto device `_ activated privacy mode. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.salto.privacy_mode_activated"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceSaltoPrivacyModeDeactivatedEvent: + """A `Salto device `_ deactivated privacy mode. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.salto.privacy_mode_deactivated"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceConnectionBecameFlakyEvent: + """Seam detected a flaky device connection. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the affected device. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["device.connection_became_flaky"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceConnectionStabilizedEvent: + """Seam detected that a previously-flaky device connection stabilized. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.connection_stabilized"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceErrorSubscriptionRequiredEvent: + """A third-party subscription is required to use all device features. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the affected device. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["device.error.subscription_required"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceErrorSubscriptionRequiredResolvedEvent: + """A third-party subscription is active or no longer required to use all device features. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.error.subscription_required.resolved"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceAccessoryKeypadConnectedEvent: + """An accessory keypad was connected to a device. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.accessory_keypad_connected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceAccessoryKeypadDisconnectedEvent: + """An accessory keypad was disconnected from a device. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_errors: Errors associated with the connected account. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar connected_account_warnings: Warnings associated with the connected account. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_errors: Errors associated with the device. + + :ivar device_id: ID of the affected device. + + :ivar device_warnings: Warnings associated with the device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class ConnectedAccountErrors(ResourceMapping): + """Errors associated with the connected account. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class ConnectedAccountWarnings(ResourceMapping): + """Warnings associated with the connected account. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceErrors(ResourceMapping): + """Errors associated with the device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: str + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceWarnings(ResourceMapping): + """Warnings associated with the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_errors: List[ConnectedAccountErrors] + connected_account_id: str + connected_account_warnings: List[ConnectedAccountWarnings] + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_errors: List[DeviceErrors] + device_id: str + device_warnings: List[DeviceWarnings] + event_description: Optional[str] + event_id: str + event_type: Literal["device.accessory_keypad_disconnected"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_errors=[ + cls.ConnectedAccountErrors.from_dict(i) + for i in d.get("connected_account_errors") or [] + ], + connected_account_id=d.get("connected_account_id", None), + connected_account_warnings=[ + cls.ConnectedAccountWarnings.from_dict(i) + for i in d.get("connected_account_warnings") or [] + ], + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_errors=[ + cls.DeviceErrors.from_dict(i) for i in d.get("device_errors") or [] + ], + device_id=d.get("device_id", None), + device_warnings=[ + cls.DeviceWarnings.from_dict(i) for i in d.get("device_warnings") or [] + ], + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class NoiseSensorNoiseThresholdTriggeredEvent: + """Extended periods of noise or noise exceeding a `threshold `_ were detected. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar minut_metadata: Metadata from Minut. + + :ivar noise_level_decibels: Detected noise level in decibels. + + :ivar noise_level_nrs: Detected noise level in Noiseaware Noise Risk Score (NRS). + + :ivar noise_threshold_id: ID of the noise threshold that was triggered. + + :ivar noise_threshold_name: Name of the noise threshold that was triggered. + + :ivar noiseaware_metadata: Metadata from Noiseaware. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["noise_sensor.noise_threshold_triggered"] + minut_metadata: Optional[Dict[str, Any]] + noise_level_decibels: Optional[float] + noise_level_nrs: Optional[float] + noise_threshold_id: Optional[str] + noise_threshold_name: Optional[str] + noiseaware_metadata: Optional[Dict[str, Any]] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + minut_metadata=DeepAttrDict(d.get("minut_metadata", None)), + noise_level_decibels=d.get("noise_level_decibels", None), + noise_level_nrs=d.get("noise_level_nrs", None), + noise_threshold_id=d.get("noise_threshold_id", None), + noise_threshold_name=d.get("noise_threshold_name", None), + noiseaware_metadata=DeepAttrDict(d.get("noiseaware_metadata", None)), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class LockLockedEvent: + """A `lock `_ was locked. + + :ivar access_code_id: ID of the access code that was used to lock the device. + + :ivar access_code_is_managed: Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + + :ivar action_attempt_id: ID of the Seam action attempt that triggered this lock. Present only when the lock was initiated through Seam (via a ``LOCK_DOOR`` action attempt). + + :ivar code: Code (PIN) that was used to lock the device, if known. Taken from the matched managed or unmanaged access code, or from the code reported by the provider when no access code matched. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar is_via_bluetooth: Whether the lock action was performed over Bluetooth by a remote client (such as the provider's mobile app), rather than a direct physical interaction or a Seam-initiated remote action. + + :ivar is_via_nfc: Whether the lock action was performed by an NFC credential tap (such as an Apple Home Key or an NFC key fob) presented to the lock, rather than a direct physical interaction or a Seam-initiated remote action. + + :ivar method: Method by which the lock was locked. ``keycode``: an access code was used (see ``access_code_id``). ``manual``: a physical action such as a thumbturn or button press. ``remote``: a remote action via an app, Bluetooth, or the Seam API (see ``action_attempt_id`` if Seam-initiated; see ``is_via_bluetooth`` or ``is_via_nfc`` for the transport). ``automatic``: triggered automatically, for example by an auto-relock timer. ``unknown``: could not be determined. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: Optional[str] + access_code_is_managed: Optional[bool] + action_attempt_id: Optional[str] + code: Optional[str] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["lock.locked"] + is_via_bluetooth: Optional[bool] + is_via_nfc: Optional[bool] + method: Literal["keycode", "manual", "automatic", "unknown", "remote", "card"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + access_code_is_managed=d.get("access_code_is_managed", None), + action_attempt_id=d.get("action_attempt_id", None), + code=d.get("code", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + is_via_bluetooth=d.get("is_via_bluetooth", None), + is_via_nfc=d.get("is_via_nfc", None), + method=d.get("method", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class LockUnlockedEvent: + """A `lock `_ was unlocked. + + :ivar access_code_id: ID of the access code that was used to unlock the affected device. + + :ivar access_code_is_managed: Whether the access code is managed by Seam (true) or unmanaged (false). Only present when access_code_id is set. + + :ivar action_attempt_id: ID of the Seam action attempt that triggered this unlock. Present only when the unlock was initiated through Seam (via an ``UNLOCK_DOOR`` action attempt). + + :ivar code: Code (PIN) that was used to unlock the affected device, if known. Taken from the matched managed or unmanaged access code, or from the code reported by the provider when no access code matched. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar is_via_bluetooth: Whether the unlock action was performed over Bluetooth by a remote client (such as the provider's mobile app), rather than a direct physical interaction or a Seam-initiated remote action. + + :ivar is_via_nfc: Whether the unlock action was performed by an NFC credential tap (such as an Apple Home Key or an NFC key fob) presented to the lock, rather than a direct physical interaction or a Seam-initiated remote action. + + :ivar method: Method by which the lock was unlocked. ``keycode``: an `access code `_ was used (see ``access_code_id``). ``manual``: a physical action such as a thumbturn or handle press. ``remote``: a remote action via an app, Bluetooth, or the Seam API (see ``action_attempt_id`` if Seam-initiated; see ``is_via_bluetooth`` or ``is_via_nfc`` for the transport). ``automatic``: triggered automatically, for example by a time-based schedule. ``unknown``: could not be determined. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + access_code_id: Optional[str] + access_code_is_managed: Optional[bool] + action_attempt_id: Optional[str] + code: Optional[str] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["lock.unlocked"] + is_via_bluetooth: Optional[bool] + is_via_nfc: Optional[bool] + method: Literal["keycode", "manual", "automatic", "unknown", "remote", "card"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + access_code_is_managed=d.get("access_code_is_managed", None), + action_attempt_id=d.get("action_attempt_id", None), + code=d.get("code", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + is_via_bluetooth=d.get("is_via_bluetooth", None), + is_via_nfc=d.get("is_via_nfc", None), + method=d.get("method", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class LockAccessDeniedEvent: + """The `lock `_ denied access to a user after one or more consecutive invalid attempts to unlock the device. + + :ivar access_code_id: ID of the access code that was used in the unlock attempts. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar reason: Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + @dataclass + class Reason(ResourceMapping): + """Why access was denied, when the provider reports a determinable cause. Omitted when unknown. + + :ivar message: Human-readable explanation of why access was denied. + + :ivar reason_code: Normalized reason a lock denied access. Provider-agnostic; not all providers report every value. + """ + + message: str + reason_code: Literal[ + "unknown_code", + "expired_code", + "blocklisted_code", + "too_many_attempts", + "blocked_by_privacy_mode", + "credential_error", + ] + + @classmethod + def from_dict(cls, d: Any): + return cls( + message=d.get("message", None), + reason_code=d.get("reason_code", None), + ) + + access_code_id: Optional[str] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: Optional[str] + event_description: Optional[str] + event_id: str + event_type: Literal["lock.access_denied"] + occurred_at: str + reason: Optional[Reason] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + access_code_id=d.get("access_code_id", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + reason=( + cls.Reason.from_dict(d.get("reason")) + if d.get("reason") is not None + else None + ), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ThermostatClimatePresetActivatedEvent: + """A thermostat `climate preset `_ was activated. + + :ivar climate_preset_key: Key of the climate preset that was activated. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar is_fallback_climate_preset: Indicates whether the climate preset that was activated is the fallback climate preset for the thermostat. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar thermostat_schedule_id: ID of the thermostat schedule that prompted the affected climate preset to be activated. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + climate_preset_key: str + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["thermostat.climate_preset_activated"] + is_fallback_climate_preset: bool + occurred_at: str + thermostat_schedule_id: Optional[str] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + climate_preset_key=d.get("climate_preset_key", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + is_fallback_climate_preset=d.get("is_fallback_climate_preset", None), + occurred_at=d.get("occurred_at", None), + thermostat_schedule_id=d.get("thermostat_schedule_id", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ThermostatManuallyAdjustedEvent: + """A `thermostat `_ was adjusted manually. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar cooling_set_point_celsius: Temperature to which the thermostat should cool (in °C). See also `Set Points `_. + + :ivar cooling_set_point_fahrenheit: Temperature to which the thermostat should cool (in °F). See also `Set Points `_. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar fan_mode_setting: Desired `fan mode setting `_, such as ``on``, ``auto``, or ``circulate``. + + :ivar heating_set_point_celsius: Temperature to which the thermostat should heat (in °C). See also `Set Points `_. + + :ivar heating_set_point_fahrenheit: Temperature to which the thermostat should heat (in °F). See also `Set Points `_. + + :ivar hvac_mode_setting: Desired `HVAC mode `_ setting, such as ``heat``, ``cool``, ``heat_cool``, or ``off``. + + :ivar method: Method used to adjust the affected thermostat manually. ``seam`` indicates that the Seam API, Seam CLI, or Seam Console was used to adjust the thermostat. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + cooling_set_point_celsius: Optional[float] + cooling_set_point_fahrenheit: Optional[float] + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["thermostat.manually_adjusted"] + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] + heating_set_point_celsius: Optional[float] + heating_set_point_fahrenheit: Optional[float] + hvac_mode_setting: Optional[Literal["off", "heat", "cool", "heat_cool", "eco"]] + method: Literal["seam", "external"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + cooling_set_point_celsius=d.get("cooling_set_point_celsius", None), + cooling_set_point_fahrenheit=d.get("cooling_set_point_fahrenheit", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + fan_mode_setting=d.get("fan_mode_setting", None), + heating_set_point_celsius=d.get("heating_set_point_celsius", None), + heating_set_point_fahrenheit=d.get("heating_set_point_fahrenheit", None), + hvac_mode_setting=d.get("hvac_mode_setting", None), + method=d.get("method", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ThermostatTemperatureThresholdExceededEvent: + """A `thermostat's `_ temperature reading exceeded the set `threshold `_. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar lower_limit_celsius: Lower temperature limit, in °C, defined by the set threshold. + + :ivar lower_limit_fahrenheit: Lower temperature limit, in °F, defined by the set threshold. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar temperature_celsius: Temperature, in °C, reported by the affected thermostat. + + :ivar temperature_fahrenheit: Temperature, in °F, reported by the affected thermostat. + + :ivar upper_limit_celsius: Upper temperature limit, in °C, defined by the set threshold. + + :ivar upper_limit_fahrenheit: Upper temperature limit, in °F, defined by the set threshold. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["thermostat.temperature_threshold_exceeded"] + lower_limit_celsius: Optional[float] + lower_limit_fahrenheit: Optional[float] + occurred_at: str + temperature_celsius: float + temperature_fahrenheit: float + upper_limit_celsius: Optional[float] + upper_limit_fahrenheit: Optional[float] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + lower_limit_celsius=d.get("lower_limit_celsius", None), + lower_limit_fahrenheit=d.get("lower_limit_fahrenheit", None), + occurred_at=d.get("occurred_at", None), + temperature_celsius=d.get("temperature_celsius", None), + temperature_fahrenheit=d.get("temperature_fahrenheit", None), + upper_limit_celsius=d.get("upper_limit_celsius", None), + upper_limit_fahrenheit=d.get("upper_limit_fahrenheit", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ThermostatTemperatureThresholdNoLongerExceededEvent: + """A `thermostat's `_ temperature reading no longer exceeds the set `threshold `_. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar lower_limit_celsius: Lower temperature limit, in °C, defined by the set threshold. + + :ivar lower_limit_fahrenheit: Lower temperature limit, in °F, defined by the set threshold. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar temperature_celsius: Temperature, in °C, reported by the affected thermostat. + + :ivar temperature_fahrenheit: Temperature, in °F, reported by the affected thermostat. + + :ivar upper_limit_celsius: Upper temperature limit, in °C, defined by the set threshold. + + :ivar upper_limit_fahrenheit: Upper temperature limit, in °F, defined by the set threshold. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["thermostat.temperature_threshold_no_longer_exceeded"] + lower_limit_celsius: Optional[float] + lower_limit_fahrenheit: Optional[float] + occurred_at: str + temperature_celsius: float + temperature_fahrenheit: float + upper_limit_celsius: Optional[float] + upper_limit_fahrenheit: Optional[float] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + lower_limit_celsius=d.get("lower_limit_celsius", None), + lower_limit_fahrenheit=d.get("lower_limit_fahrenheit", None), + occurred_at=d.get("occurred_at", None), + temperature_celsius=d.get("temperature_celsius", None), + temperature_fahrenheit=d.get("temperature_fahrenheit", None), + upper_limit_celsius=d.get("upper_limit_celsius", None), + upper_limit_fahrenheit=d.get("upper_limit_fahrenheit", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ThermostatTemperatureReachedSetPointEvent: + """A `thermostat's `_ temperature reading is within 1 °C of the configured cooling or heating `set point `_. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar desired_temperature_celsius: Desired temperature, in °C, defined by the affected thermostat's cooling or heating set point. + + :ivar desired_temperature_fahrenheit: Desired temperature, in °F, defined by the affected thermostat's cooling or heating set point. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar temperature_celsius: Temperature, in °C, reported by the affected thermostat. + + :ivar temperature_fahrenheit: Temperature, in °F, reported by the affected thermostat. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + desired_temperature_celsius: Optional[float] + desired_temperature_fahrenheit: Optional[float] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["thermostat.temperature_reached_set_point"] + occurred_at: str + temperature_celsius: float + temperature_fahrenheit: float + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + desired_temperature_celsius=d.get("desired_temperature_celsius", None), + desired_temperature_fahrenheit=d.get( + "desired_temperature_fahrenheit", None + ), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + temperature_celsius=d.get("temperature_celsius", None), + temperature_fahrenheit=d.get("temperature_fahrenheit", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class ThermostatTemperatureChangedEvent: + """A `thermostat's `_ reported temperature changed by at least 1 °C. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar temperature_celsius: Temperature, in °C, reported by the affected thermostat. + + :ivar temperature_fahrenheit: Temperature, in °F, reported by the affected thermostat. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["thermostat.temperature_changed"] + occurred_at: str + temperature_celsius: float + temperature_fahrenheit: float + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + temperature_celsius=d.get("temperature_celsius", None), + temperature_fahrenheit=d.get("temperature_fahrenheit", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceNameChangedEvent: + """The name of a device was changed. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar device_name: The new name of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + device_name: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.name_changed"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + device_name=d.get("device_name", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class CameraActivatedEvent: + """A camera was activated, for example, by motion detection. + + :ivar activation_reason: The reason the camera was activated. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar image_url: URL to a thumbnail image captured at the time of activation. + + :ivar motion_sub_type: Sub-type of motion detected, if available. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar video_url: URL to a short video clip captured at the time of activation. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + activation_reason: Literal["motion_detected"] + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["camera.activated"] + image_url: Optional[str] + motion_sub_type: Optional[Literal["human", "vehicle", "package", "other"]] + occurred_at: str + video_url: Optional[str] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + activation_reason=d.get("activation_reason", None), + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + image_url=d.get("image_url", None), + motion_sub_type=d.get("motion_sub_type", None), + occurred_at=d.get("occurred_at", None), + video_url=d.get("video_url", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class DeviceDoorbellRangEvent: + """A doorbell button was pressed on a device. + + :ivar connected_account_custom_metadata: Custom metadata of the connected account, present when connected_account_id is provided. + + :ivar connected_account_id: ID of the connected account associated with the event. + + :ivar created_at: Date and time at which the event was created. + + :ivar customer_key: The customer key associated with the device, if any. + + :ivar device_custom_metadata: Custom metadata of the device, present when device_id is provided. + + :ivar device_id: ID of the affected device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar image_url: URL to a thumbnail image captured at the time the doorbell was pressed. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar video_url: URL to a short video clip captured at the time the doorbell was pressed. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + connected_account_custom_metadata: Optional[Dict[str, Union[str, bool]]] + connected_account_id: str + created_at: str + customer_key: Optional[str] + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["device.doorbell_rang"] + image_url: Optional[str] + occurred_at: str + video_url: Optional[str] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + connected_account_custom_metadata=DeepAttrDict( + d.get("connected_account_custom_metadata", None) + ), + connected_account_id=d.get("connected_account_id", None), + created_at=d.get("created_at", None), + customer_key=d.get("customer_key", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + image_url=d.get("image_url", None), + occurred_at=d.get("occurred_at", None), + video_url=d.get("video_url", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class PhoneDeactivatedEvent: + """A phone device was deactivated. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_custom_metadata: Custom metadata of the device; present when device_id is provided. + + :ivar device_id: ID of the affected phone device. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + created_at: str + device_custom_metadata: Optional[Dict[str, Union[str, bool]]] + device_id: str + event_description: Optional[str] + event_id: str + event_type: Literal["phone.deactivated"] + occurred_at: str + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + device_custom_metadata=DeepAttrDict(d.get("device_custom_metadata", None)), + device_id=d.get("device_id", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class SpaceDeviceMembershipChangedEvent: + """A device was added or removed from a space. + + :ivar acs_entrance_ids: IDs of all ACS entrances currently attached to the space. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_ids: IDs of all devices currently attached to the space. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: Type of the event. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar space_id: ID of the affected space. + + :ivar space_key: Unique key for the space within the workspace. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_entrance_ids: List[str] + created_at: str + device_ids: List[str] + event_description: Optional[str] + event_id: str + event_type: Literal["space.device_membership_changed"] + occurred_at: str + space_id: str + space_key: Optional[str] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_entrance_ids=d.get("acs_entrance_ids", None), + created_at=d.get("created_at", None), + device_ids=d.get("device_ids", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + space_id=d.get("space_id", None), + space_key=d.get("space_key", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class SpaceCreatedEvent: + """A space was created. + + :ivar acs_entrance_ids: IDs of all ACS entrances attached to the space when it was created. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_ids: IDs of all devices attached to the space when it was created. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: Type of the event. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar space_id: ID of the affected space. + + :ivar space_key: Unique key for the space within the workspace. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_entrance_ids: List[str] + created_at: str + device_ids: List[str] + event_description: Optional[str] + event_id: str + event_type: Literal["space.created"] + occurred_at: str + space_id: str + space_key: Optional[str] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_entrance_ids=d.get("acs_entrance_ids", None), + created_at=d.get("created_at", None), + device_ids=d.get("device_ids", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + space_id=d.get("space_id", None), + space_key=d.get("space_key", None), + workspace_id=d.get("workspace_id", None), + ) + + +@dataclass +class SpaceDeletedEvent: + """A space was deleted. + + :ivar acs_entrance_ids: IDs of all ACS entrances currently attached to the space when it was deleted. + + :ivar created_at: Date and time at which the event was created. + + :ivar device_ids: IDs of all devices attached to the space when it was deleted. + + :ivar event_description: Human-readable description of the event. Persisted when the event is created (so the creating code, including a provider, can supply a tailored description) and otherwise derived from the event. + + :ivar event_id: ID of the event. + + :ivar event_type: Type of the event. + + :ivar occurred_at: Date and time at which the event occurred. + + :ivar space_id: ID of the affected space. + + :ivar space_key: Unique key for the space within the workspace. + + :ivar workspace_id: ID of the workspace associated with the event.""" + + acs_entrance_ids: List[str] + created_at: str + device_ids: List[str] + event_description: Optional[str] + event_id: str + event_type: Literal["space.deleted"] + occurred_at: str + space_id: str + space_key: Optional[str] + workspace_id: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + acs_entrance_ids=d.get("acs_entrance_ids", None), + created_at=d.get("created_at", None), + device_ids=d.get("device_ids", None), + event_description=d.get("event_description", None), + event_id=d.get("event_id", None), + event_type=d.get("event_type", None), + occurred_at=d.get("occurred_at", None), + space_id=d.get("space_id", None), + space_key=d.get("space_key", None), + workspace_id=d.get("workspace_id", None), + ) + + +SeamEvent = Union[ + AccessCodeCreatedEvent, + AccessCodeChangedEvent, + AccessCodeNameChangedEvent, + AccessCodeCodeChangedEvent, + AccessCodeTimeFrameChangedEvent, + AccessCodeMutationsRequestedEvent, + AccessCodeScheduledOnDeviceEvent, + AccessCodeSetOnDeviceEvent, + AccessCodeRemovedFromDeviceEvent, + AccessCodeDelayInSettingOnDeviceEvent, + AccessCodeFailedToSetOnDeviceEvent, + AccessCodeDeletedEvent, + AccessCodeDelayInRemovingFromDeviceEvent, + AccessCodeFailedToRemoveFromDeviceEvent, + AccessCodeModifiedExternalToSeamEvent, + AccessCodeDeletedExternalToSeamEvent, + AccessCodeBackupAccessCodePulledEvent, + AccessCodeUnmanagedConvertedToManagedEvent, + AccessCodeUnmanagedFailedToConvertToManagedEvent, + AccessCodeUnmanagedCreatedEvent, + AccessCodeUnmanagedRemovedEvent, + AccessGrantCreatedEvent, + AccessGrantDeletedEvent, + AccessGrantAccessGrantedToAllDoorsEvent, + AccessGrantAccessGrantedToDoorEvent, + AccessGrantAccessToDoorLostEvent, + AccessGrantAccessTimesChangedEvent, + AccessGrantCouldNotCreateRequestedAccessMethodsEvent, + AccessMethodIssuedEvent, + AccessMethodRevokedEvent, + AccessMethodCardEncodingRequiredEvent, + AccessMethodDeletedEvent, + AccessMethodReissuedEvent, + AccessMethodCreatedEvent, + AccessMethodDelayInIssuingEvent, + AccessMethodFailedToIssueEvent, + AcsSystemConnectedEvent, + AcsSystemAddedEvent, + AcsSystemDisconnectedEvent, + AcsCredentialDeletedEvent, + AcsCredentialIssuedEvent, + AcsCredentialReissuedEvent, + AcsCredentialInvalidatedEvent, + AcsUserCreatedEvent, + AcsUserDeletedEvent, + AcsEncoderAddedEvent, + AcsEncoderRemovedEvent, + AcsAccessGroupDeletedEvent, + AcsEntranceAddedEvent, + AcsEntranceRemovedEvent, + ClientSessionDeletedEvent, + ConnectedAccountConnectedEvent, + ConnectedAccountCreatedEvent, + ConnectedAccountSuccessfulLoginEvent, + ConnectedAccountDisconnectedEvent, + ConnectedAccountCompletedFirstSyncEvent, + ConnectedAccountDeletedEvent, + ConnectedAccountCompletedFirstSyncAfterReconnectionEvent, + ConnectedAccountReauthorizationRequestedEvent, + ActionAttemptLockDoorSucceededEvent, + ActionAttemptLockDoorFailedEvent, + ActionAttemptUnlockDoorSucceededEvent, + ActionAttemptUnlockDoorFailedEvent, + ActionAttemptSimulateKeypadCodeEntrySucceededEvent, + ActionAttemptSimulateKeypadCodeEntryFailedEvent, + ActionAttemptSimulateManualLockViaKeypadSucceededEvent, + ActionAttemptSimulateManualLockViaKeypadFailedEvent, + ConnectWebviewLoginSucceededEvent, + ConnectWebviewLoginFailedEvent, + DeviceConnectedEvent, + DeviceAddedEvent, + DeviceConvertedToUnmanagedEvent, + DeviceUnmanagedConvertedToManagedEvent, + DeviceUnmanagedConnectedEvent, + DeviceDisconnectedEvent, + DeviceUnmanagedDisconnectedEvent, + DeviceTamperedEvent, + DeviceLowBatteryEvent, + DeviceBatteryStatusChangedEvent, + DeviceRemovedEvent, + DeviceDeletedEvent, + DeviceThirdPartyIntegrationDetectedEvent, + DeviceThirdPartyIntegrationNoLongerDetectedEvent, + DeviceSaltoPrivacyModeActivatedEvent, + DeviceSaltoPrivacyModeDeactivatedEvent, + DeviceConnectionBecameFlakyEvent, + DeviceConnectionStabilizedEvent, + DeviceErrorSubscriptionRequiredEvent, + DeviceErrorSubscriptionRequiredResolvedEvent, + DeviceAccessoryKeypadConnectedEvent, + DeviceAccessoryKeypadDisconnectedEvent, + NoiseSensorNoiseThresholdTriggeredEvent, + LockLockedEvent, + LockUnlockedEvent, + LockAccessDeniedEvent, + ThermostatClimatePresetActivatedEvent, + ThermostatManuallyAdjustedEvent, + ThermostatTemperatureThresholdExceededEvent, + ThermostatTemperatureThresholdNoLongerExceededEvent, + ThermostatTemperatureReachedSetPointEvent, + ThermostatTemperatureChangedEvent, + DeviceNameChangedEvent, + CameraActivatedEvent, + DeviceDoorbellRangEvent, + PhoneDeactivatedEvent, + SpaceDeviceMembershipChangedEvent, + SpaceCreatedEvent, + SpaceDeletedEvent, +] + +_SEAM_EVENT_VARIANTS: Dict[str, Any] = { + "access_code.created": AccessCodeCreatedEvent, + "access_code.changed": AccessCodeChangedEvent, + "access_code.name_changed": AccessCodeNameChangedEvent, + "access_code.code_changed": AccessCodeCodeChangedEvent, + "access_code.time_frame_changed": AccessCodeTimeFrameChangedEvent, + "access_code.mutations_requested": AccessCodeMutationsRequestedEvent, + "access_code.scheduled_on_device": AccessCodeScheduledOnDeviceEvent, + "access_code.set_on_device": AccessCodeSetOnDeviceEvent, + "access_code.removed_from_device": AccessCodeRemovedFromDeviceEvent, + "access_code.delay_in_setting_on_device": AccessCodeDelayInSettingOnDeviceEvent, + "access_code.failed_to_set_on_device": AccessCodeFailedToSetOnDeviceEvent, + "access_code.deleted": AccessCodeDeletedEvent, + "access_code.delay_in_removing_from_device": AccessCodeDelayInRemovingFromDeviceEvent, + "access_code.failed_to_remove_from_device": AccessCodeFailedToRemoveFromDeviceEvent, + "access_code.modified_external_to_seam": AccessCodeModifiedExternalToSeamEvent, + "access_code.deleted_external_to_seam": AccessCodeDeletedExternalToSeamEvent, + "access_code.backup_access_code_pulled": AccessCodeBackupAccessCodePulledEvent, + "access_code.unmanaged.converted_to_managed": AccessCodeUnmanagedConvertedToManagedEvent, + "access_code.unmanaged.failed_to_convert_to_managed": AccessCodeUnmanagedFailedToConvertToManagedEvent, + "access_code.unmanaged.created": AccessCodeUnmanagedCreatedEvent, + "access_code.unmanaged.removed": AccessCodeUnmanagedRemovedEvent, + "access_grant.created": AccessGrantCreatedEvent, + "access_grant.deleted": AccessGrantDeletedEvent, + "access_grant.access_granted_to_all_doors": AccessGrantAccessGrantedToAllDoorsEvent, + "access_grant.access_granted_to_door": AccessGrantAccessGrantedToDoorEvent, + "access_grant.access_to_door_lost": AccessGrantAccessToDoorLostEvent, + "access_grant.access_times_changed": AccessGrantAccessTimesChangedEvent, + "access_grant.could_not_create_requested_access_methods": AccessGrantCouldNotCreateRequestedAccessMethodsEvent, + "access_method.issued": AccessMethodIssuedEvent, + "access_method.revoked": AccessMethodRevokedEvent, + "access_method.card_encoding_required": AccessMethodCardEncodingRequiredEvent, + "access_method.deleted": AccessMethodDeletedEvent, + "access_method.reissued": AccessMethodReissuedEvent, + "access_method.created": AccessMethodCreatedEvent, + "access_method.delay_in_issuing": AccessMethodDelayInIssuingEvent, + "access_method.failed_to_issue": AccessMethodFailedToIssueEvent, + "acs_system.connected": AcsSystemConnectedEvent, + "acs_system.added": AcsSystemAddedEvent, + "acs_system.disconnected": AcsSystemDisconnectedEvent, + "acs_credential.deleted": AcsCredentialDeletedEvent, + "acs_credential.issued": AcsCredentialIssuedEvent, + "acs_credential.reissued": AcsCredentialReissuedEvent, + "acs_credential.invalidated": AcsCredentialInvalidatedEvent, + "acs_user.created": AcsUserCreatedEvent, + "acs_user.deleted": AcsUserDeletedEvent, + "acs_encoder.added": AcsEncoderAddedEvent, + "acs_encoder.removed": AcsEncoderRemovedEvent, + "acs_access_group.deleted": AcsAccessGroupDeletedEvent, + "acs_entrance.added": AcsEntranceAddedEvent, + "acs_entrance.removed": AcsEntranceRemovedEvent, + "client_session.deleted": ClientSessionDeletedEvent, + "connected_account.connected": ConnectedAccountConnectedEvent, + "connected_account.created": ConnectedAccountCreatedEvent, + "connected_account.successful_login": ConnectedAccountSuccessfulLoginEvent, + "connected_account.disconnected": ConnectedAccountDisconnectedEvent, + "connected_account.completed_first_sync": ConnectedAccountCompletedFirstSyncEvent, + "connected_account.deleted": ConnectedAccountDeletedEvent, + "connected_account.completed_first_sync_after_reconnection": ConnectedAccountCompletedFirstSyncAfterReconnectionEvent, + "connected_account.reauthorization_requested": ConnectedAccountReauthorizationRequestedEvent, + "action_attempt.lock_door.succeeded": ActionAttemptLockDoorSucceededEvent, + "action_attempt.lock_door.failed": ActionAttemptLockDoorFailedEvent, + "action_attempt.unlock_door.succeeded": ActionAttemptUnlockDoorSucceededEvent, + "action_attempt.unlock_door.failed": ActionAttemptUnlockDoorFailedEvent, + "action_attempt.simulate_keypad_code_entry.succeeded": ActionAttemptSimulateKeypadCodeEntrySucceededEvent, + "action_attempt.simulate_keypad_code_entry.failed": ActionAttemptSimulateKeypadCodeEntryFailedEvent, + "action_attempt.simulate_manual_lock_via_keypad.succeeded": ActionAttemptSimulateManualLockViaKeypadSucceededEvent, + "action_attempt.simulate_manual_lock_via_keypad.failed": ActionAttemptSimulateManualLockViaKeypadFailedEvent, + "connect_webview.login_succeeded": ConnectWebviewLoginSucceededEvent, + "connect_webview.login_failed": ConnectWebviewLoginFailedEvent, + "device.connected": DeviceConnectedEvent, + "device.added": DeviceAddedEvent, + "device.converted_to_unmanaged": DeviceConvertedToUnmanagedEvent, + "device.unmanaged.converted_to_managed": DeviceUnmanagedConvertedToManagedEvent, + "device.unmanaged.connected": DeviceUnmanagedConnectedEvent, + "device.disconnected": DeviceDisconnectedEvent, + "device.unmanaged.disconnected": DeviceUnmanagedDisconnectedEvent, + "device.tampered": DeviceTamperedEvent, + "device.low_battery": DeviceLowBatteryEvent, + "device.battery_status_changed": DeviceBatteryStatusChangedEvent, + "device.removed": DeviceRemovedEvent, + "device.deleted": DeviceDeletedEvent, + "device.third_party_integration_detected": DeviceThirdPartyIntegrationDetectedEvent, + "device.third_party_integration_no_longer_detected": DeviceThirdPartyIntegrationNoLongerDetectedEvent, + "device.salto.privacy_mode_activated": DeviceSaltoPrivacyModeActivatedEvent, + "device.salto.privacy_mode_deactivated": DeviceSaltoPrivacyModeDeactivatedEvent, + "device.connection_became_flaky": DeviceConnectionBecameFlakyEvent, + "device.connection_stabilized": DeviceConnectionStabilizedEvent, + "device.error.subscription_required": DeviceErrorSubscriptionRequiredEvent, + "device.error.subscription_required.resolved": DeviceErrorSubscriptionRequiredResolvedEvent, + "device.accessory_keypad_connected": DeviceAccessoryKeypadConnectedEvent, + "device.accessory_keypad_disconnected": DeviceAccessoryKeypadDisconnectedEvent, + "noise_sensor.noise_threshold_triggered": NoiseSensorNoiseThresholdTriggeredEvent, + "lock.locked": LockLockedEvent, + "lock.unlocked": LockUnlockedEvent, + "lock.access_denied": LockAccessDeniedEvent, + "thermostat.climate_preset_activated": ThermostatClimatePresetActivatedEvent, + "thermostat.manually_adjusted": ThermostatManuallyAdjustedEvent, + "thermostat.temperature_threshold_exceeded": ThermostatTemperatureThresholdExceededEvent, + "thermostat.temperature_threshold_no_longer_exceeded": ThermostatTemperatureThresholdNoLongerExceededEvent, + "thermostat.temperature_reached_set_point": ThermostatTemperatureReachedSetPointEvent, + "thermostat.temperature_changed": ThermostatTemperatureChangedEvent, + "device.name_changed": DeviceNameChangedEvent, + "camera.activated": CameraActivatedEvent, + "device.doorbell_rang": DeviceDoorbellRangEvent, + "phone.deactivated": PhoneDeactivatedEvent, + "space.device_membership_changed": SpaceDeviceMembershipChangedEvent, + "space.created": SpaceCreatedEvent, + "space.deleted": SpaceDeletedEvent, +} + + +def seam_event_from_dict(d: Any) -> SeamEvent: + """Deserialize a known event_type variant. + + Unknown discriminator values return ``DeepAttrDict`` so payloads from a + newer API remain readable. The static return type covers known variants. + """ + variant = _SEAM_EVENT_VARIANTS.get(d.get("event_type")) + if variant is None: + return cast(SeamEvent, DeepAttrDict(d)) + return variant.from_dict(d) diff --git a/seam/resources/unmanaged_access_code.py b/seam/resources/unmanaged_access_code.py index a2e7676c..bd7ada1b 100644 --- a/seam/resources/unmanaged_access_code.py +++ b/seam/resources/unmanaged_access_code.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class UnmanagedAccessCode: """Represents an `unmanaged smart lock access code `_. @@ -95,8 +102,8 @@ def from_dict(cls, d: Any): ) @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access code `_. + class ProviderIssueError(ResourceMapping): + """Indicates a provider-specific issue that prevents the access code from being set or managed. Check the error message for details. :ivar created_at: Date and time at which Seam created the error. @@ -105,55 +112,99 @@ class Errors(ResourceMapping): :ivar is_access_code_error: Indicates that this is an access code error. :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar managed_access_code_id: ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + created_at: Optional[str] + error_code: Literal["provider_issue"] + is_access_code_error: Literal[True] + message: str - :ivar unmanaged_access_code_id: ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) - :ivar change_type: Indicates the type of external modification. ``modified`` means the code's PIN or schedule was changed. ``removed`` means the code was deleted from the device. + @dataclass + class FailedToSetOnDeviceError(ResourceMapping): + """Failed to set code on device. - :ivar modified_fields: List of fields that were changed externally, with their previous and new values. + :ivar created_at: Date and time at which Seam created the error. - :ivar is_connected_account_error: + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar is_device_error: + :ivar is_access_code_error: Indicates that this is an access code error. - :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. """ - @dataclass - class ModifiedFields(ResourceMapping): - """List of fields that were changed externally, with their previous and new values. + created_at: Optional[str] + error_code: Literal["failed_to_set_on_device"] + is_access_code_error: Literal[True] + message: str - :ivar field: The name of the field that was changed (e.g. ``code``, ``starts_at``, ``ends_at``). + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) - :ivar from_: The previous value of the field. + @dataclass + class FailedToRemoveFromDeviceError(ResourceMapping): + """Failed to remove code from device. - :ivar to: The new value of the field.""" + :ivar created_at: Date and time at which Seam created the error. - field: str - from_: Optional[str] - to: Optional[str] + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - @classmethod - def from_dict(cls, d: Any): - return cls( - field=d.get("field", None), - from_=d.get("from", None), - to=d.get("to", None), - ) + :ivar is_access_code_error: Indicates that this is an access code error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ created_at: Optional[str] - error_code: str - is_access_code_error: Optional[Literal[True]] + error_code: Literal["failed_to_remove_from_device"] + is_access_code_error: Literal[True] message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) + + @dataclass + class DuplicateCodeOnDeviceError(ResourceMapping): + """Duplicate access code detected on device. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_access_code_error: Indicates that this is an access code error. + + :ivar managed_access_code_id: ID of the managed access code that conflicts with this managed access code, when Seam can identify it. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + + :ivar unmanaged_access_code_id: ID of the unmanaged access code that conflicts with this managed access code, when Seam can identify it. + """ + + created_at: Optional[str] + error_code: Literal["duplicate_code_on_device"] + is_access_code_error: Literal[True] managed_access_code_id: Optional[str] + message: str unmanaged_access_code_id: Optional[str] - change_type: Optional[str] - modified_fields: Optional[List[ModifiedFields]] - is_connected_account_error: Optional[bool] - is_device_error: Optional[Literal[False, True]] - is_bridge_error: Optional[bool] @classmethod def from_dict(cls, d: Any): @@ -161,31 +212,52 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), error_code=d.get("error_code", None), is_access_code_error=d.get("is_access_code_error", None), - message=d.get("message", None), managed_access_code_id=d.get("managed_access_code_id", None), + message=d.get("message", None), unmanaged_access_code_id=d.get("unmanaged_access_code_id", None), - change_type=d.get("change_type", None), - modified_fields=[ - cls.ModifiedFields.from_dict(i) - for i in d.get("modified_fields") or [] - ], - is_connected_account_error=d.get("is_connected_account_error", None), - is_device_error=d.get("is_device_error", None), - is_bridge_error=d.get("is_bridge_error", None), ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access code `_. + class NoSpaceForAccessCodeOnDeviceError(ResourceMapping): + """No space for access code on device. - :ivar created_at: Date and time at which Seam created the warning. + :ivar created_at: Date and time at which Seam created the error. - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + :ivar is_access_code_error: Indicates that this is an access code error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: Optional[str] + error_code: Literal["no_space_for_access_code_on_device"] + is_access_code_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) + + @dataclass + class ConflictingExternalModificationError(ResourceMapping): + """Code was modified or removed externally after Seam successfully set it on the device. The external change conflicts with the state that Seam is trying to apply, so Seam will attempt to set the code on the device again. :ivar change_type: Indicates the type of external modification. ``modified`` means the code's PIN or schedule was changed. ``removed`` means the code was deleted from the device. + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_access_code_error: Indicates that this is an access code error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + :ivar modified_fields: List of fields that were changed externally, with their previous and new values. """ @@ -211,25 +283,812 @@ def from_dict(cls, d: Any): to=d.get("to", None), ) + change_type: Optional[Literal["modified", "removed"]] created_at: Optional[str] + error_code: Literal["conflicting_external_modification"] + is_access_code_error: Literal[True] message: str - warning_code: str - change_type: Optional[str] modified_fields: Optional[List[ModifiedFields]] @classmethod def from_dict(cls, d: Any): return cls( + change_type=d.get("change_type", None), created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), - change_type=d.get("change_type", None), modified_fields=[ cls.ModifiedFields.from_dict(i) for i in d.get("modified_fields") or [] ], ) + @dataclass + class AccessCodeInactiveError(ResourceMapping): + """Indicates that the access code is disabled or inactive on the device. The code exists but will not grant access until re-enabled. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_access_code_error: Indicates that this is an access code error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: Optional[str] + error_code: Literal["access_code_inactive"] + is_access_code_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_access_code_error=d.get("is_access_code_error", None), + message=d.get("message", None), + ) + + @dataclass + class AccountDisconnectedError(ResourceMapping): + """Indicates that the account is disconnected. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["account_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitExceededError(ResourceMapping): + """Indicates that the Salto site user limit has been reached. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["salto_ks_subscription_limit_exceeded"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class InsufficientPermissionsError(ResourceMapping): + """Indicates that Seam's integration user does not have sufficient permissions on the provider's system to which this device belongs, so Seam cannot manage access codes or unlock the device. See the error message for specifics, then either reauthorize the connected account in Seam or grant the integration user the required permissions in the provider's system. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["insufficient_permissions"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DormakabaSitesDisconnectedError(ResourceMapping): + """Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. + + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["dormakaba_sites_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceOfflineError(ResourceMapping): + """Indicates that the device is offline. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["device_offline"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceRemovedError(ResourceMapping): + """Indicates that the device has been removed. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["device_removed"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class HubDisconnectedError(ResourceMapping): + """Indicates that the hub is disconnected. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["hub_disconnected"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class DeviceDisconnectedError(ResourceMapping): + """Indicates that the device is disconnected. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["device_disconnected"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class EmptyBackupAccessCodePoolError(ResourceMapping): + """Indicates that the `backup access code pool `_ is empty. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["empty_backup_access_code_pool"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class AugustLockNotAuthorizedError(ResourceMapping): + """Indicates that the user is not authorized to use the August lock. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["august_lock_not_authorized"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class MissingDeviceCredentialsError(ResourceMapping): + """Indicates that device credentials are missing. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["missing_device_credentials"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class AuxiliaryHeatRunningError(ResourceMapping): + """Indicates that the auxiliary heat is running. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["auxiliary_heat_running"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class SubscriptionRequiredError(ResourceMapping): + """Indicates that a subscription is required to connect. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["subscription_required"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class BridgeDisconnectedError(ResourceMapping): + """Indicates that the Seam API cannot communicate with `Seam Bridge `_, for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also `Troubleshooting Your Access Control System `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + + :ivar is_connected_account_error: Indicates whether the error is related specifically to the connected account. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["bridge_disconnected"] + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_bridge_error=d.get("is_bridge_error", None), + is_connected_account_error=d.get("is_connected_account_error", None), + message=d.get("message", None), + ) + + @dataclass + class CodeRotatesPeriodicallyWarning(ResourceMapping): + """The access code's PIN rotates periodically when the code is renewed. Retrieve the latest code before each use. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["code_rotates_periodically"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TimeFrameAdjustedForUnknownTimeZoneWarning(ResourceMapping): + """The device's time zone is unknown and this code's time frame crosses a daylight-saving transition in at least one plausible time zone. A 1-hour safety buffer has been applied to the side of the time frame affected by the transition (``ends_at`` for spring-forward, ``starts_at`` for fall-back) so the code stays active through the shift — the code may be usable up to 1 hour beyond your requested window. Set the device's time zone via ``/devices/report_provider_metadata`` to clear the buffer and guarantee exact handling. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["time_frame_adjusted_for_unknown_time_zone"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ExternalModificationInEffectWarning(ResourceMapping): + """Code was modified or removed externally after Seam successfully set it on the device. External modification is allowed for this code, so the externally modified state is being honored. + + :ivar change_type: Indicates the type of external modification. ``modified`` means the code's PIN or schedule was changed. ``removed`` means the code was deleted from the device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar modified_fields: List of fields that were changed externally, with their previous and new values. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + @dataclass + class ModifiedFields(ResourceMapping): + """List of fields that were changed externally, with their previous and new values. + + :ivar field: The name of the field that was changed (e.g. ``code``, ``starts_at``, ``ends_at``). + + :ivar from_: The previous value of the field. + + :ivar to: The new value of the field.""" + + field: str + from_: Optional[str] + to: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + field=d.get("field", None), + from_=d.get("from", None), + to=d.get("to", None), + ) + + change_type: Optional[Literal["modified", "removed"]] + created_at: Optional[str] + message: str + modified_fields: Optional[List[ModifiedFields]] + warning_code: Literal["external_modification_in_effect"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + change_type=d.get("change_type", None), + created_at=d.get("created_at", None), + message=d.get("message", None), + modified_fields=[ + cls.ModifiedFields.from_dict(i) + for i in d.get("modified_fields") or [] + ], + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DelayInSettingOnDeviceWarning(ResourceMapping): + """Delay in setting code on device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["delay_in_setting_on_device"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DelayInRemovingFromDeviceWarning(ResourceMapping): + """Delay in removing code from device. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["delay_in_removing_from_device"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ThirdPartyIntegrationDetectedWarning(ResourceMapping): + """Third-party integration detected that may cause access codes to fail. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["third_party_integration_detected"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class IglooAlgopinMustBeUsedWithin24HoursWarning(ResourceMapping): + """Algopins must be used within 24 hours. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["igloo_algopin_must_be_used_within_24_hours"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ManagementTransferredWarning(ResourceMapping): + """Management was transferred to another workspace. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["management_transferred"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UsingBackupAccessCodeWarning(ResourceMapping): + """A backup access code has been pulled and is being used in place of this access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["using_backup_access_code"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class BeingDeletedWarning(ResourceMapping): + """Access code is being deleted. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithAccessCodeWarning(ResourceMapping): + """An unknown issue occurred with the access code. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: Optional[str] + message: str + warning_code: Literal["unknown_issue_with_access_code"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + Errors = Union[ + ProviderIssueError, + FailedToSetOnDeviceError, + FailedToRemoveFromDeviceError, + DuplicateCodeOnDeviceError, + NoSpaceForAccessCodeOnDeviceError, + ConflictingExternalModificationError, + AccessCodeInactiveError, + AccountDisconnectedError, + SaltoKsSubscriptionLimitExceededError, + InsufficientPermissionsError, + DormakabaSitesDisconnectedError, + DeviceOfflineError, + DeviceRemovedError, + HubDisconnectedError, + DeviceDisconnectedError, + EmptyBackupAccessCodePoolError, + AugustLockNotAuthorizedError, + MissingDeviceCredentialsError, + AuxiliaryHeatRunningError, + SubscriptionRequiredError, + BridgeDisconnectedError, + ] + _ErrorsVariants = { + "provider_issue": ProviderIssueError, + "failed_to_set_on_device": FailedToSetOnDeviceError, + "failed_to_remove_from_device": FailedToRemoveFromDeviceError, + "duplicate_code_on_device": DuplicateCodeOnDeviceError, + "no_space_for_access_code_on_device": NoSpaceForAccessCodeOnDeviceError, + "conflicting_external_modification": ConflictingExternalModificationError, + "access_code_inactive": AccessCodeInactiveError, + "account_disconnected": AccountDisconnectedError, + "salto_ks_subscription_limit_exceeded": SaltoKsSubscriptionLimitExceededError, + "insufficient_permissions": InsufficientPermissionsError, + "dormakaba_sites_disconnected": DormakabaSitesDisconnectedError, + "device_offline": DeviceOfflineError, + "device_removed": DeviceRemovedError, + "hub_disconnected": HubDisconnectedError, + "device_disconnected": DeviceDisconnectedError, + "empty_backup_access_code_pool": EmptyBackupAccessCodePoolError, + "august_lock_not_authorized": AugustLockNotAuthorizedError, + "missing_device_credentials": MissingDeviceCredentialsError, + "auxiliary_heat_running": AuxiliaryHeatRunningError, + "subscription_required": SubscriptionRequiredError, + "bridge_disconnected": BridgeDisconnectedError, + } + + Warnings = Union[ + CodeRotatesPeriodicallyWarning, + TimeFrameAdjustedForUnknownTimeZoneWarning, + ExternalModificationInEffectWarning, + DelayInSettingOnDeviceWarning, + DelayInRemovingFromDeviceWarning, + ThirdPartyIntegrationDetectedWarning, + IglooAlgopinMustBeUsedWithin24HoursWarning, + ManagementTransferredWarning, + UsingBackupAccessCodeWarning, + BeingDeletedWarning, + UnknownIssueWithAccessCodeWarning, + ] + _WarningsVariants = { + "code_rotates_periodically": CodeRotatesPeriodicallyWarning, + "time_frame_adjusted_for_unknown_time_zone": TimeFrameAdjustedForUnknownTimeZoneWarning, + "external_modification_in_effect": ExternalModificationInEffectWarning, + "delay_in_setting_on_device": DelayInSettingOnDeviceWarning, + "delay_in_removing_from_device": DelayInRemovingFromDeviceWarning, + "third_party_integration_detected": ThirdPartyIntegrationDetectedWarning, + "igloo_algopin_must_be_used_within_24_hours": IglooAlgopinMustBeUsedWithin24HoursWarning, + "management_transferred": ManagementTransferredWarning, + "using_backup_access_code": UsingBackupAccessCodeWarning, + "being_deleted": BeingDeletedWarning, + "unknown_issue_with_access_code": UnknownIssueWithAccessCodeWarning, + } + access_code_id: str cannot_be_managed: Optional[Literal[True]] cannot_delete_unmanaged_access_code: Optional[Literal[True]] @@ -242,8 +1101,8 @@ def from_dict(cls, d: Any): is_managed: Literal[False] name: Optional[str] starts_at: Optional[str] - status: str - type: str + status: Literal["set", "unset"] + type: Literal["time_bound", "ongoing"] warnings: List[Warnings] workspace_id: str @@ -266,12 +1125,18 @@ def from_dict(cls, d: Any): else None ), ends_at=d.get("ends_at", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], is_managed=d.get("is_managed", None), name=d.get("name", None), starts_at=d.get("starts_at", None), status=d.get("status", None), type=d.get("type", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/unmanaged_access_grant.py b/seam/resources/unmanaged_access_grant.py index 9fc48ad4..6dfb161c 100644 --- a/seam/resources/unmanaged_access_grant.py +++ b/seam/resources/unmanaged_access_grant.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class UnmanagedAccessGrant: """Represents an unmanaged Access Grant. Unmanaged Access Grants do not have client sessions, instant keys, customization profiles, or keys. @@ -41,8 +48,8 @@ class UnmanagedAccessGrant: :ivar workspace_id: ID of the Seam workspace associated with the Access Grant.""" @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access grant `_. + class CannotCreateRequestedAccessMethodsError(ResourceMapping): + """Indicates that Seam could not create one or more of the requested access methods for the access grant. :ivar created_at: Date and time at which Seam created the error. @@ -54,7 +61,7 @@ class Errors(ResourceMapping): """ created_at: str - error_code: str + error_code: Literal["cannot_create_requested_access_methods"] message: str missing_device_ids: Optional[List[str]] @@ -68,79 +75,134 @@ def from_dict(cls, d: Any): ) @dataclass - class PendingMutations(ResourceMapping): - """List of pending mutations for the access grant. This shows updates that are in progress. + class UpdatingSpacesPendingMutation(ResourceMapping): + """Seam is in the process of updating the devices/spaces associated with this access grant. :ivar created_at: Date and time at which the mutation was created. - :ivar from_: + :ivar from_: Previous location configuration. :ivar message: Detailed description of the mutation. - :ivar mutation_code: + :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the spaces (devices) associated with this access grant. - :ivar to: - - :ivar access_method_ids: IDs of the access methods being updated.""" + :ivar to: New location configuration.""" @dataclass class From(ResourceMapping): - """ + """Previous location configuration. + + :ivar device_ids: Previous device IDs where access codes existed.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + @dataclass + class To(ResourceMapping): + """New location configuration. + + :ivar common_code_key: Common code key to ensure PIN code reuse across devices. + + :ivar device_ids: New device IDs where access codes should be created.""" + + common_code_key: Optional[str] + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + common_code_key=d.get("common_code_key", None), + device_ids=d.get("device_ids", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["updating_spaces"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingAccessTimesPendingMutation(ResourceMapping): + """Seam is in the process of updating the access times for this access grant. + + :ivar access_method_ids: IDs of the access methods being updated. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous access time configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the access times for this access grant. - :ivar device_ids: Previous device IDs where access codes existed. + :ivar to: New access time configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous access time configuration. :ivar ends_at: Previous end time for access. :ivar starts_at: Previous start time for access.""" - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) @dataclass class To(ResourceMapping): - """ - - :ivar common_code_key: Common code key to ensure PIN code reuse across devices. - - :ivar device_ids: New device IDs where access codes should be created. + """New access time configuration. :ivar ends_at: New end time for access. :ivar starts_at: New start time for access.""" - common_code_key: Optional[str] - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - common_code_key=d.get("common_code_key", None), - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) + access_method_ids: List[str] created_at: str from_: Optional[From] message: str - mutation_code: str + mutation_code: Literal["updating_access_times"] to: Optional[To] - access_method_ids: Optional[List[str]] @classmethod def from_dict(cls, d: Any): return cls( + access_method_ids=d.get("access_method_ids", None), created_at=d.get("created_at", None), from_=( cls.From.from_dict(d.get("from")) @@ -150,7 +212,6 @@ def from_dict(cls, d: Any): message=d.get("message", None), mutation_code=d.get("mutation_code", None), to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, - access_method_ids=d.get("access_method_ids", None), ) @dataclass @@ -175,7 +236,7 @@ class RequestedAccessMethods(ResourceMapping): created_at: str display_name: str instant_key_max_use_count: Optional[int] - mode: str + mode: Literal["code", "card", "mobile_key", "cloud_key"] @classmethod def from_dict(cls, d: Any): @@ -189,26 +250,62 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access grant `_. + class BeingDeletedWarning(ResourceMapping): + """Indicates that the `access grant `_ is being deleted. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ - :ivar failed_devices: Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + created_at: str + message: str + warning_code: Literal["being_deleted"] - :ivar access_method_ids: IDs of the access methods being updated. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) - :ivar device_id: + @dataclass + class UnderprovisionedAccessWarning(ResourceMapping): + """Indicates that the access grant should have access to more locations than it currently does. Access methods are being created for the missing locations. - :ivar new_code: The new PIN code that was assigned instead. + :ivar created_at: Date and time at which Seam created the warning. - :ivar original_code: The originally requested PIN code that was unavailable. + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. - :ivar reason: Specific reason why the grant's times are not programmable on the device. + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["underprovisioned_access"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class OverprovisionedAccessWarning(ResourceMapping): + """Indicates that the access grant has access to locations it should not have. Access methods are being removed from the extra locations. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar failed_devices: Devices whose access codes could not be revoked during reconciliation. Present when the provider does not support revoking an offline access code (e.g. Dormakaba oracode with exhausted override budget). + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. """ @dataclass @@ -234,32 +331,176 @@ def from_dict(cls, d: Any): ) created_at: str - message: str - warning_code: str failed_devices: Optional[List[FailedDevices]] - access_method_ids: Optional[List[str]] - device_id: Optional[str] - new_code: Optional[str] - original_code: Optional[str] - reason: Optional[str] + message: str + warning_code: Literal["overprovisioned_access"] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), - message=d.get("message", None), - warning_code=d.get("warning_code", None), failed_devices=[ cls.FailedDevices.from_dict(i) for i in d.get("failed_devices") or [] ], + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UpdatingAccessTimesWarning(ResourceMapping): + """Indicates that the access times for this `access grant `_ are being updated. + + :ivar access_method_ids: IDs of the access methods being updated. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + access_method_ids: List[str] + created_at: str + message: str + warning_code: Literal["updating_access_times"] + + @classmethod + def from_dict(cls, d: Any): + return cls( access_method_ids=d.get("access_method_ids", None), + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class RequestedCodeUnavailableWarning(ResourceMapping): + """Indicates that the requested PIN code was already in use on a device, so a different code was assigned. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar device_id: ID of the device where the requested code was unavailable. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar new_code: The new PIN code that was assigned instead. + + :ivar original_code: The originally requested PIN code that was unavailable. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + device_id: str + message: str + new_code: str + original_code: str + warning_code: Literal["requested_code_unavailable"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), device_id=d.get("device_id", None), + message=d.get("message", None), new_code=d.get("new_code", None), original_code=d.get("original_code", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceDoesNotSupportAccessCodesWarning(ResourceMapping): + """Indicates that a device in the access grant does not support access codes and was excluded from code materialization. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar device_id: ID of the device that does not support access codes. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + device_id: str + message: str + warning_code: Literal["device_does_not_support_access_codes"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceTimeConstraintsViolatedWarning(ResourceMapping): + """Indicates that a device in the access grant cannot program an access code for the grant's time range because of device-specific time constraints. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar device_id: ID of the device whose time constraints the access grant violates. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar reason: Specific reason why the grant's times are not programmable on the device. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + device_id: str + message: str + reason: Literal[ + "duration_exceeds_max", "times_do_not_match_slots", "ongoing_not_supported" + ] + warning_code: Literal["device_time_constraints_violated"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + device_id=d.get("device_id", None), + message=d.get("message", None), reason=d.get("reason", None), + warning_code=d.get("warning_code", None), ) + Errors = Union[CannotCreateRequestedAccessMethodsError] + _ErrorsVariants = { + "cannot_create_requested_access_methods": CannotCreateRequestedAccessMethodsError, + } + + PendingMutations = Union[ + UpdatingSpacesPendingMutation, UpdatingAccessTimesPendingMutation + ] + _PendingMutationsVariants = { + "updating_spaces": UpdatingSpacesPendingMutation, + "updating_access_times": UpdatingAccessTimesPendingMutation, + } + + Warnings = Union[ + BeingDeletedWarning, + UnderprovisionedAccessWarning, + OverprovisionedAccessWarning, + UpdatingAccessTimesWarning, + RequestedCodeUnavailableWarning, + DeviceDoesNotSupportAccessCodesWarning, + DeviceTimeConstraintsViolatedWarning, + ] + _WarningsVariants = { + "being_deleted": BeingDeletedWarning, + "underprovisioned_access": UnderprovisionedAccessWarning, + "overprovisioned_access": OverprovisionedAccessWarning, + "updating_access_times": UpdatingAccessTimesWarning, + "requested_code_unavailable": RequestedCodeUnavailableWarning, + "device_does_not_support_access_codes": DeviceDoesNotSupportAccessCodesWarning, + "device_time_constraints_violated": DeviceTimeConstraintsViolatedWarning, + } + access_grant_id: str access_method_ids: List[str] created_at: str @@ -285,11 +526,16 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), display_name=d.get("display_name", None), ends_at=d.get("ends_at", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], location_ids=d.get("location_ids", None), name=d.get("name", None), pending_mutations=[ - cls.PendingMutations.from_dict(i) + _from_discriminated_dict( + i, cls._PendingMutationsVariants, "mutation_code" + ) for i in d.get("pending_mutations") or [] ], requested_access_methods=[ @@ -300,6 +546,9 @@ def from_dict(cls, d: Any): space_ids=d.get("space_ids", None), starts_at=d.get("starts_at", None), user_identity_id=d.get("user_identity_id", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/unmanaged_access_method.py b/seam/resources/unmanaged_access_method.py index 32b3a56e..16338e36 100644 --- a/seam/resources/unmanaged_access_method.py +++ b/seam/resources/unmanaged_access_method.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class UnmanagedAccessMethod: """Represents an unmanaged access method. Unmanaged access methods do not have client sessions, instant keys, customization profiles, or keys. @@ -39,8 +46,8 @@ class UnmanagedAccessMethod: :ivar workspace_id: ID of the Seam workspace associated with the access method.""" @dataclass - class Errors(ResourceMapping): - """Errors associated with the `access method `_. + class FailedToIssueError(ResourceMapping): + """Indicates that Seam was unable to issue this `access method `_ before its access grant started, so the recipient may be unable to access the space. This usually points to a problem that needs attention, such as an offline or disconnected device. Seam keeps retrying, and this error clears automatically if the access method is eventually issued. :ivar created_at: Date and time at which Seam created the error. @@ -50,7 +57,7 @@ class Errors(ResourceMapping): """ created_at: str - error_code: str + error_code: Literal["failed_to_issue"] message: str @classmethod @@ -62,59 +69,175 @@ def from_dict(cls, d: Any): ) @dataclass - class PendingMutations(ResourceMapping): - """Pending mutations for the `access method `_. Indicates operations that are in progress. + class ProvisioningAccessPendingMutation(ResourceMapping): + """Seam is in the process of provisioning access for this access method on new devices. :ivar created_at: Date and time at which the mutation was created. - :ivar from_: + :ivar from_: Previous device configuration. :ivar message: Detailed description of the mutation. - :ivar mutation_code: + :ivar mutation_code: Mutation code to indicate that Seam is in the process of provisioning access for this access method on new devices. - :ivar to:""" + :ivar to: New device configuration.""" @dataclass class From(ResourceMapping): - """ + """Previous device configuration. + + :ivar device_ids: Previous device IDs where access was provisioned.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + @dataclass + class To(ResourceMapping): + """New device configuration. + + :ivar device_ids: New device IDs where access is being provisioned.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["provisioning_access"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class RevokingAccessPendingMutation(ResourceMapping): + """Seam is in the process of revoking access for this access method from devices. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous device configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of revoking access for this access method from devices. + + :ivar to: New device configuration.""" + + @dataclass + class From(ResourceMapping): + """Previous device configuration. + + :ivar device_ids: Previous device IDs where access existed.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + @dataclass + class To(ResourceMapping): + """New device configuration. + + :ivar device_ids: New device IDs where access should remain.""" + + device_ids: List[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + device_ids=d.get("device_ids", None), + ) + + created_at: str + from_: Optional[From] + message: str + mutation_code: Literal["revoking_access"] + to: Optional[To] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + from_=( + cls.From.from_dict(d.get("from")) + if d.get("from") is not None + else None + ), + message=d.get("message", None), + mutation_code=d.get("mutation_code", None), + to=cls.To.from_dict(d.get("to")) if d.get("to") is not None else None, + ) + + @dataclass + class UpdatingAccessTimesPendingMutation(ResourceMapping): + """Seam is in the process of updating the access times for this access method. + + :ivar created_at: Date and time at which the mutation was created. + + :ivar from_: Previous access time configuration. + + :ivar message: Detailed description of the mutation. + + :ivar mutation_code: Mutation code to indicate that Seam is in the process of updating the access times for this access method. + + :ivar to: New access time configuration.""" - :ivar device_ids: + @dataclass + class From(ResourceMapping): + """Previous access time configuration. :ivar ends_at: Previous end time for access. :ivar starts_at: Previous start time for access.""" - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) @dataclass class To(ResourceMapping): - """ - - :ivar device_ids: + """New access time configuration. :ivar ends_at: New end time for access. :ivar starts_at: New start time for access.""" - device_ids: Optional[List[str]] ends_at: Optional[str] starts_at: Optional[str] @classmethod def from_dict(cls, d: Any): return cls( - device_ids=d.get("device_ids", None), ends_at=d.get("ends_at", None), starts_at=d.get("starts_at", None), ) @@ -122,7 +245,7 @@ def from_dict(cls, d: Any): created_at: str from_: Optional[From] message: str - mutation_code: str + mutation_code: Literal["updating_access_times"] to: Optional[To] @classmethod @@ -140,32 +263,130 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Warnings associated with the `access method `_. + class BeingDeletedWarning(ResourceMapping): + """Indicates that the `access method `_ is being deleted. :ivar created_at: Date and time at which Seam created the warning. :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UpdatingAccessTimesWarning(ResourceMapping): + """Indicates that the access times for this `access method `_ are being updated. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["updating_access_times"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class PulledBackupAccessCodeWarning(ResourceMapping): + """Indicates that all attempts to create an access code on this device before the start time failed and a backup access code was used to ensure access was provided in time. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. :ivar original_access_method_id: ID of the original access method from which this backup access method was split, if applicable. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. """ created_at: str message: str - warning_code: str original_access_method_id: Optional[str] + warning_code: Literal["pulled_backup_access_code"] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), original_access_method_id=d.get("original_access_method_id", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DelayInIssuingWarning(ResourceMapping): + """Indicates that Seam has not yet issued this `access method `_, even though its access grant is about to begin, so access may not be ready when the recipient arrives. Seam is still attempting to issue it, and this warning clears automatically once issuance succeeds. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["delay_in_issuing"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), ) + Errors = Union[FailedToIssueError] + _ErrorsVariants = { + "failed_to_issue": FailedToIssueError, + } + + PendingMutations = Union[ + ProvisioningAccessPendingMutation, + RevokingAccessPendingMutation, + UpdatingAccessTimesPendingMutation, + ] + _PendingMutationsVariants = { + "provisioning_access": ProvisioningAccessPendingMutation, + "revoking_access": RevokingAccessPendingMutation, + "updating_access_times": UpdatingAccessTimesPendingMutation, + } + + Warnings = Union[ + BeingDeletedWarning, + UpdatingAccessTimesWarning, + PulledBackupAccessCodeWarning, + DelayInIssuingWarning, + ] + _WarningsVariants = { + "being_deleted": BeingDeletedWarning, + "updating_access_times": UpdatingAccessTimesWarning, + "pulled_backup_access_code": PulledBackupAccessCodeWarning, + "delay_in_issuing": DelayInIssuingWarning, + } + access_method_id: str code: Optional[str] created_at: str @@ -177,7 +398,7 @@ def from_dict(cls, d: Any): is_ready_for_assignment: Optional[bool] is_ready_for_encoding: Optional[bool] issued_at: Optional[str] - mode: str + mode: Literal["code", "card", "mobile_key", "cloud_key"] pending_mutations: List[PendingMutations] warnings: List[Warnings] workspace_id: str @@ -189,7 +410,10 @@ def from_dict(cls, d: Any): code=d.get("code", None), created_at=d.get("created_at", None), display_name=d.get("display_name", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], is_assignment_required=d.get("is_assignment_required", None), is_encoding_required=d.get("is_encoding_required", None), is_issued=d.get("is_issued", None), @@ -198,9 +422,14 @@ def from_dict(cls, d: Any): issued_at=d.get("issued_at", None), mode=d.get("mode", None), pending_mutations=[ - cls.PendingMutations.from_dict(i) + _from_discriminated_dict( + i, cls._PendingMutationsVariants, "mutation_code" + ) for i in d.get("pending_mutations") or [] ], - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/unmanaged_device.py b/seam/resources/unmanaged_device.py index 3952059c..a43429a0 100644 --- a/seam/resources/unmanaged_device.py +++ b/seam/resources/unmanaged_device.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class UnmanagedDevice: """Represents an `unmanaged device `_. An unmanaged device has a limited set of visible properties and a subset of supported events. You cannot control an unmanaged device. Any `access codes `_ on an unmanaged device are unmanaged. To control an unmanaged device with Seam, `convert it to a managed device `_. @@ -74,28 +81,25 @@ class UnmanagedDevice: """ @dataclass - class Errors(ResourceMapping): - """Array of errors associated with the device. Each error object within the array contains two fields: ``error_code`` and ``message``. ``error_code`` is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. ``message`` provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + class AccountDisconnectedError(ResourceMapping): + """Indicates that the account is disconnected. :ivar created_at: Date and time at which Seam created the error. :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar is_connected_account_error: + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar is_device_error: + :ivar is_device_error: Indicates that the error is not a device error. :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. - - :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. """ created_at: str - error_code: str - is_connected_account_error: Optional[bool] - is_device_error: Optional[Literal[False, True]] + error_code: Literal["account_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] message: str - is_bridge_error: Optional[bool] @classmethod def from_dict(cls, d: Any): @@ -105,241 +109,1297 @@ def from_dict(cls, d: Any): is_connected_account_error=d.get("is_connected_account_error", None), is_device_error=d.get("is_device_error", None), message=d.get("message", None), - is_bridge_error=d.get("is_bridge_error", None), ) @dataclass - class Location(ResourceMapping): - """Location information for the device. + class SaltoKsSubscriptionLimitExceededError(ResourceMapping): + """Indicates that the Salto site user limit has been reached. - :ivar location_name: Name of the device location. + :ivar created_at: Date and time at which Seam created the error. - :ivar room_name: Name of the room within the device location, when the provider reports one. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar time_zone: Time zone of the device location. + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. + :ivar is_device_error: Indicates that the error is not a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. """ - location_name: Optional[str] - room_name: Optional[str] - time_zone: Optional[str] - timezone: Optional[str] + created_at: str + error_code: Literal["salto_ks_subscription_limit_exceeded"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str @classmethod def from_dict(cls, d: Any): return cls( - location_name=d.get("location_name", None), - room_name=d.get("room_name", None), - time_zone=d.get("time_zone", None), - timezone=d.get("timezone", None), + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), ) @dataclass - class Properties(ResourceMapping): - """properties of the device. - - :ivar accessory_keypad: Accessory keypad properties and state. + class InsufficientPermissionsError(ResourceMapping): + """Indicates that Seam's integration user does not have sufficient permissions on the provider's system to which this device belongs, so Seam cannot manage access codes or unlock the device. See the error message for specifics, then either reauthorize the connected account in Seam or grant the integration user the required permissions in the provider's system. - :ivar battery: Represents the current status of the battery charge level. + :ivar created_at: Date and time at which Seam created the error. - :ivar battery_level: Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar image_alt_text: Alt text for the device image. + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar image_url: Image URL for the device. + :ivar is_device_error: Indicates that the error is not a device error. - :ivar manufacturer: Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar model: Device model-related properties. + created_at: str + error_code: Literal["insufficient_permissions"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str - :ivar name: Deprecated: use device.display_name instead Name of the device. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar offline_access_codes_enabled: Deprecated: use device.can_program_offline_access_codes Indicates whether it is currently possible to use offline access codes for the device. + @dataclass + class DormakabaSitesDisconnectedError(ResourceMapping): + """Indicates that one or more dormakaba sites associated with the connected account could not be connected. Contact dormakaba support. - :ivar online: Indicates whether the device is online. + :ivar created_at: Date and time at which Seam created the error. - :ivar online_access_codes_enabled: Deprecated: use device.can_program_online_access_codes Indicates whether it is currently possible to use online access codes for the device. - """ + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - @dataclass - class AccessoryKeypad(ResourceMapping): - """Accessory keypad properties and state. + :ivar is_connected_account_error: Indicates that the error is a `connected account `_ error. - :ivar battery: Keypad battery properties. + :ivar is_device_error: Indicates that the error is not a device error. - :ivar is_connected: Indicates if an accessory keypad is connected to the device. - """ + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - @dataclass - class Battery(ResourceMapping): - """Keypad battery properties. + created_at: str + error_code: Literal["dormakaba_sites_disconnected"] + is_connected_account_error: Literal[True] + is_device_error: Literal[False] + message: str - :ivar level:""" + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_connected_account_error=d.get("is_connected_account_error", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - level: float + @dataclass + class DeviceOfflineError(ResourceMapping): + """Indicates that the device is offline. - @classmethod - def from_dict(cls, d: Any): - return cls( - level=d.get("level", None), - ) + :ivar created_at: Date and time at which Seam created the error. - battery: Optional[Battery] - is_connected: bool + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - @classmethod - def from_dict(cls, d: Any): - return cls( - battery=( - cls.Battery.from_dict(d.get("battery")) - if d.get("battery") is not None - else None - ), - is_connected=d.get("is_connected", None), - ) + :ivar is_device_error: Indicates that the error is a device error. - @dataclass - class Battery(ResourceMapping): - """Represents the current status of the battery charge level. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar level: Battery charge level as a value between 0 and 1, inclusive. + created_at: str + error_code: Literal["device_offline"] + is_device_error: Literal[True] + message: str - :ivar status: Represents the current status of the battery charge level. Values are ``critical``, which indicates an extremely low level, suggesting imminent shutdown or an urgent need for charging; ``low``, which signifies that the battery is under the preferred threshold and should be charged soon; ``good``, which denotes a satisfactory charge level, adequate for normal use without the immediate need for recharging; and ``full``, which represents a battery that is fully charged, providing the maximum duration of usage. - """ + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - level: float - status: str + @dataclass + class DeviceRemovedError(ResourceMapping): + """Indicates that the device has been removed. - @classmethod - def from_dict(cls, d: Any): - return cls( - level=d.get("level", None), - status=d.get("status", None), - ) + :ivar created_at: Date and time at which Seam created the error. - @dataclass - class Model(ResourceMapping): - """Device model-related properties. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar accessory_keypad_supported: Deprecated: use device.properties.model.can_connect_accessory_keypad + :ivar is_device_error: Indicates that the error is a device error. - :ivar can_connect_accessory_keypad: Indicates whether the device can connect a accessory keypad. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - :ivar display_name: Display name of the device model. + created_at: str + error_code: Literal["device_removed"] + is_device_error: Literal[True] + message: str - :ivar has_built_in_keypad: Indicates whether the device has a built in accessory keypad. + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) - :ivar manufacturer_display_name: Display name that corresponds to the manufacturer-specific terminology for the device. + @dataclass + class HubDisconnectedError(ResourceMapping): + """Indicates that the hub is disconnected. - :ivar offline_access_codes_supported: Deprecated: use device.can_program_offline_access_codes. + :ivar created_at: Date and time at which Seam created the error. - :ivar online_access_codes_supported: Deprecated: use device.can_program_online_access_codes. - """ + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - accessory_keypad_supported: Optional[bool] - can_connect_accessory_keypad: Optional[bool] - display_name: str - has_built_in_keypad: Optional[bool] - manufacturer_display_name: str - offline_access_codes_supported: Optional[bool] - online_access_codes_supported: Optional[bool] + :ivar is_device_error: Indicates that the error is a device error. - @classmethod - def from_dict(cls, d: Any): - return cls( - accessory_keypad_supported=d.get( - "accessory_keypad_supported", None - ), - can_connect_accessory_keypad=d.get( - "can_connect_accessory_keypad", None - ), - display_name=d.get("display_name", None), - has_built_in_keypad=d.get("has_built_in_keypad", None), - manufacturer_display_name=d.get("manufacturer_display_name", None), - offline_access_codes_supported=d.get( - "offline_access_codes_supported", None - ), - online_access_codes_supported=d.get( - "online_access_codes_supported", None - ), - ) + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ - accessory_keypad: Optional[AccessoryKeypad] - battery: Optional[Battery] - battery_level: Optional[float] - image_alt_text: Optional[str] - image_url: Optional[str] - manufacturer: Optional[str] - model: Optional[Model] - name: str - offline_access_codes_enabled: Optional[bool] - online: bool - online_access_codes_enabled: Optional[bool] + created_at: str + error_code: Literal["hub_disconnected"] + is_device_error: Literal[True] + message: str @classmethod def from_dict(cls, d: Any): return cls( - accessory_keypad=( - cls.AccessoryKeypad.from_dict(d.get("accessory_keypad")) - if d.get("accessory_keypad") is not None - else None - ), - battery=( - cls.Battery.from_dict(d.get("battery")) - if d.get("battery") is not None - else None - ), - battery_level=d.get("battery_level", None), - image_alt_text=d.get("image_alt_text", None), - image_url=d.get("image_url", None), - manufacturer=d.get("manufacturer", None), - model=( - cls.Model.from_dict(d.get("model")) - if d.get("model") is not None - else None - ), - name=d.get("name", None), - offline_access_codes_enabled=d.get( - "offline_access_codes_enabled", None - ), - online=d.get("online", None), - online_access_codes_enabled=d.get("online_access_codes_enabled", None), + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), ) @dataclass - class Warnings(ResourceMapping): - """Array of warnings associated with the device. Each warning object within the array contains two fields: ``warning_code`` and ``message``. ``warning_code`` is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. ``message`` provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. - - :ivar created_at: Date and time at which Seam created the warning. + class DeviceDisconnectedError(ResourceMapping): + """Indicates that the device is disconnected. - :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + :ivar created_at: Date and time at which Seam created the error. - :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. - :ivar active_access_code_count: Number of active access codes on the device when the warning was set. + :ivar is_device_error: Indicates that the error is a device error. - :ivar max_active_access_code_count: Maximum number of active access codes supported by the device. + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. """ created_at: str + error_code: Literal["device_disconnected"] + is_device_error: Literal[True] message: str - warning_code: str - active_access_code_count: Optional[int] - max_active_access_code_count: Optional[int] @classmethod def from_dict(cls, d: Any): return cls( created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), message=d.get("message", None), - warning_code=d.get("warning_code", None), - active_access_code_count=d.get("active_access_code_count", None), - max_active_access_code_count=d.get( - "max_active_access_code_count", None - ), ) + @dataclass + class EmptyBackupAccessCodePoolError(ResourceMapping): + """Indicates that the `backup access code pool `_ is empty. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["empty_backup_access_code_pool"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class AugustLockNotAuthorizedError(ResourceMapping): + """Indicates that the user is not authorized to use the August lock. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["august_lock_not_authorized"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class MissingDeviceCredentialsError(ResourceMapping): + """Indicates that device credentials are missing. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["missing_device_credentials"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class AuxiliaryHeatRunningError(ResourceMapping): + """Indicates that the auxiliary heat is running. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["auxiliary_heat_running"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class SubscriptionRequiredError(ResourceMapping): + """Indicates that a subscription is required to connect. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_device_error: Indicates that the error is a device error. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["subscription_required"] + is_device_error: Literal[True] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_device_error=d.get("is_device_error", None), + message=d.get("message", None), + ) + + @dataclass + class BridgeDisconnectedError(ResourceMapping): + """Indicates that the Seam API cannot communicate with `Seam Bridge `_, for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also `Troubleshooting Your Access Control System `_. + + :ivar created_at: Date and time at which Seam created the error. + + :ivar error_code: Unique identifier of the type of error. Enables quick recognition and categorization of the issue. + + :ivar is_bridge_error: Indicates whether the error is related to `Seam Bridge `_. + + :ivar is_connected_account_error: Indicates whether the error is related specifically to the connected account. + + :ivar message: Detailed description of the error. Provides insights into the issue and potentially how to rectify it. + """ + + created_at: str + error_code: Literal["bridge_disconnected"] + is_bridge_error: Optional[bool] + is_connected_account_error: Optional[bool] + message: str + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + error_code=d.get("error_code", None), + is_bridge_error=d.get("is_bridge_error", None), + is_connected_account_error=d.get("is_connected_account_error", None), + message=d.get("message", None), + ) + + @dataclass + class Location(ResourceMapping): + """Location information for the device. + + :ivar location_name: Name of the device location. + + :ivar room_name: Name of the room within the device location, when the provider reports one. + + :ivar time_zone: Time zone of the device location. + + :ivar timezone: Deprecated: Use ``time_zone`` instead. Time zone of the device location. + """ + + location_name: Optional[str] + room_name: Optional[str] + time_zone: Optional[str] + timezone: Optional[str] + + @classmethod + def from_dict(cls, d: Any): + return cls( + location_name=d.get("location_name", None), + room_name=d.get("room_name", None), + time_zone=d.get("time_zone", None), + timezone=d.get("timezone", None), + ) + + @dataclass + class Properties(ResourceMapping): + """properties of the device. + + :ivar accessory_keypad: Accessory keypad properties and state. + + :ivar battery: Represents the current status of the battery charge level. + + :ivar battery_level: Indicates the battery level of the device as a decimal value between 0 and 1, inclusive. + + :ivar image_alt_text: Alt text for the device image. + + :ivar image_url: Image URL for the device. + + :ivar manufacturer: Manufacturer of the device. When a device, such as a smart lock, is connected through a smart hub, the manufacturer of the device might be different from that of the smart hub. + + :ivar model: Device model-related properties. + + :ivar name: Deprecated: use device.display_name instead Name of the device. + + :ivar offline_access_codes_enabled: Deprecated: use device.can_program_offline_access_codes Indicates whether it is currently possible to use offline access codes for the device. + + :ivar online: Indicates whether the device is online. + + :ivar online_access_codes_enabled: Deprecated: use device.can_program_online_access_codes Indicates whether it is currently possible to use online access codes for the device. + """ + + @dataclass + class AccessoryKeypad(ResourceMapping): + """Accessory keypad properties and state. + + :ivar battery: Keypad battery properties. + + :ivar is_connected: Indicates if an accessory keypad is connected to the device. + """ + + @dataclass + class Battery(ResourceMapping): + """Keypad battery properties. + + :ivar level:""" + + level: float + + @classmethod + def from_dict(cls, d: Any): + return cls( + level=d.get("level", None), + ) + + battery: Optional[Battery] + is_connected: bool + + @classmethod + def from_dict(cls, d: Any): + return cls( + battery=( + cls.Battery.from_dict(d.get("battery")) + if d.get("battery") is not None + else None + ), + is_connected=d.get("is_connected", None), + ) + + @dataclass + class Battery(ResourceMapping): + """Represents the current status of the battery charge level. + + :ivar level: Battery charge level as a value between 0 and 1, inclusive. + + :ivar status: Represents the current status of the battery charge level. Values are ``critical``, which indicates an extremely low level, suggesting imminent shutdown or an urgent need for charging; ``low``, which signifies that the battery is under the preferred threshold and should be charged soon; ``good``, which denotes a satisfactory charge level, adequate for normal use without the immediate need for recharging; and ``full``, which represents a battery that is fully charged, providing the maximum duration of usage. + """ + + level: float + status: Literal["critical", "low", "good", "full"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + level=d.get("level", None), + status=d.get("status", None), + ) + + @dataclass + class Model(ResourceMapping): + """Device model-related properties. + + :ivar accessory_keypad_supported: Deprecated: use device.properties.model.can_connect_accessory_keypad + + :ivar can_connect_accessory_keypad: Indicates whether the device can connect a accessory keypad. + + :ivar display_name: Display name of the device model. + + :ivar has_built_in_keypad: Indicates whether the device has a built in accessory keypad. + + :ivar manufacturer_display_name: Display name that corresponds to the manufacturer-specific terminology for the device. + + :ivar offline_access_codes_supported: Deprecated: use device.can_program_offline_access_codes. + + :ivar online_access_codes_supported: Deprecated: use device.can_program_online_access_codes. + """ + + accessory_keypad_supported: Optional[bool] + can_connect_accessory_keypad: Optional[bool] + display_name: str + has_built_in_keypad: Optional[bool] + manufacturer_display_name: str + offline_access_codes_supported: Optional[bool] + online_access_codes_supported: Optional[bool] + + @classmethod + def from_dict(cls, d: Any): + return cls( + accessory_keypad_supported=d.get( + "accessory_keypad_supported", None + ), + can_connect_accessory_keypad=d.get( + "can_connect_accessory_keypad", None + ), + display_name=d.get("display_name", None), + has_built_in_keypad=d.get("has_built_in_keypad", None), + manufacturer_display_name=d.get("manufacturer_display_name", None), + offline_access_codes_supported=d.get( + "offline_access_codes_supported", None + ), + online_access_codes_supported=d.get( + "online_access_codes_supported", None + ), + ) + + accessory_keypad: Optional[AccessoryKeypad] + battery: Optional[Battery] + battery_level: Optional[float] + image_alt_text: Optional[str] + image_url: Optional[str] + manufacturer: Optional[str] + model: Optional[Model] + name: str + offline_access_codes_enabled: Optional[bool] + online: bool + online_access_codes_enabled: Optional[bool] + + @classmethod + def from_dict(cls, d: Any): + return cls( + accessory_keypad=( + cls.AccessoryKeypad.from_dict(d.get("accessory_keypad")) + if d.get("accessory_keypad") is not None + else None + ), + battery=( + cls.Battery.from_dict(d.get("battery")) + if d.get("battery") is not None + else None + ), + battery_level=d.get("battery_level", None), + image_alt_text=d.get("image_alt_text", None), + image_url=d.get("image_url", None), + manufacturer=d.get("manufacturer", None), + model=( + cls.Model.from_dict(d.get("model")) + if d.get("model") is not None + else None + ), + name=d.get("name", None), + offline_access_codes_enabled=d.get( + "offline_access_codes_enabled", None + ), + online=d.get("online", None), + online_access_codes_enabled=d.get("online_access_codes_enabled", None), + ) + + @dataclass + class PartialBackupAccessCodePoolWarning(ResourceMapping): + """Indicates that the backup access code is unhealthy. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["partial_backup_access_code_pool"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ManyActiveBackupCodesWarning(ResourceMapping): + """Indicates that there are too many backup codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["many_active_backup_codes"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ThirdPartyIntegrationDetectedWarning(ResourceMapping): + """Indicates that a third-party integration has been detected. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["third_party_integration_detected"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TtlockLockGatewayUnlockingNotEnabledWarning(ResourceMapping): + """Indicates that the Remote Unlock feature is not enabled in the settings." + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["ttlock_lock_gateway_unlocking_not_enabled"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TtlockWeakGatewaySignalWarning(ResourceMapping): + """Indicates that the gateway signal is weak. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["ttlock_weak_gateway_signal"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class PowerSavingModeWarning(ResourceMapping): + """Indicates that the device is in power saving mode and may have limited functionality. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["power_saving_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TemperatureThresholdExceededWarning(ResourceMapping): + """Indicates that the temperature threshold has been exceeded. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["temperature_threshold_exceeded"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceCommunicationDegradedWarning(ResourceMapping): + """Indicates that the device appears to be unresponsive. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["device_communication_degraded"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ScheduledMaintenanceWindowWarning(ResourceMapping): + """Indicates that a scheduled maintenance window has been detected. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["scheduled_maintenance_window"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class DeviceHasFlakyConnectionWarning(ResourceMapping): + """Indicates that the device has a flaky connection. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["device_has_flaky_connection"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsOfficeModeWarning(ResourceMapping): + """Indicates that the Salto KS lock is in Office Mode. Access Codes will not unlock doors. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_office_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsPrivacyModeWarning(ResourceMapping): + """Indicates that the Salto KS lock is in Privacy Mode. Access Codes will not unlock doors. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_privacy_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class PrivacyModeWarning(ResourceMapping): + """Indicates that the lock is in Privacy Mode. Access codes and remote unlock are blocked until Privacy Mode is disabled. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["privacy_mode"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsSubscriptionLimitAlmostReachedWarning(ResourceMapping): + """Indicates that the Salto KS site has exceeded 80% of the maximum number of allowed users. Increase your subscription limit or delete some users from your site. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_subscription_limit_almost_reached"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class SaltoKsLockAccessCodeSupportRemovedWarning(ResourceMapping): + """Indicates that a change in the reported device model has been detected for this Salto KS lock, which may occur after an IQ hub reset. Access code support may be affected. See https://help.getseam.com/articles/5098842588-salto-ks-lock-loses-access-code-support for troubleshooting steps. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["salto_ks_lock_access_code_support_removed"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnknownIssueWithPhoneWarning(ResourceMapping): + """Indicates that an unknown issue occurred while syncing the state of the phone with the provider. This issue may affect the proper functioning of the phone. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["unknown_issue_with_phone"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class LocklyTimeZoneNotConfiguredWarning(ResourceMapping): + """Indicates that Seam detected that the Lockly device does not have a time zone configured. Time-bound codes may not work as expected. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["lockly_time_zone_not_configured"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UltraloqTimeZoneUnknownWarning(ResourceMapping): + """Indicates that Seam does not know the time zone of the Ultraloq device. Set a time zone to enable time-bound access codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["ultraloq_time_zone_unknown"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TimeZoneUnknownWarning(ResourceMapping): + """Indicates that Seam does not know the device's time zone. Set a time zone to enable time-bound access codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["time_zone_unknown"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TimeZoneMismatchWarning(ResourceMapping): + """Indicates that the device's configured time zone does not match its hardware UTC offset. Time-bound access codes may activate at the wrong local time. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["time_zone_mismatch"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class TwoNDeviceMissingTimezoneWarning(ResourceMapping): + """Indicates that the 2N device does not have a time zone configured. Configure a time zone on the device to enable access codes. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["two_n_device_missing_timezone"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class HubRequiredForAdditionalCapabilitiesWarning(ResourceMapping): + """Indicates that a hub or relay must be connected to unlock additional capabilities such as remote unlock. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["hub_required_for_additional_capabilities"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class ProviderIssueWarning(ResourceMapping): + """Indicates a provider-specific issue that may affect device functionality. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["provider_issue"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class KeynestUnsupportedLockerWarning(ResourceMapping): + """Indicates that the key is in a locker that does not support the access codes API. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["keynest_unsupported_locker"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class AccessoryKeypadSetupRequiredWarning(ResourceMapping): + """Indicates that the accessory keypad exists, but is not linked to the Igloohome Bridge. Online access code programming will fail until the keypad is linked to the Igloohome Bridge in the Igloohome app. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["accessory_keypad_setup_required"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class UnreliableOnlineStatusWarning(ResourceMapping): + """Indicates that the device may optimistically be reported as online because the provider does not reliably report its online status. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["unreliable_online_status"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class MaxAccessCodesReachedWarning(ResourceMapping): + """Indicates that the device has reached its maximum number of active access codes. Delete existing codes before creating new ones. + + :ivar active_access_code_count: Number of active access codes on the device when the warning was set. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar max_active_access_code_count: Maximum number of active access codes supported by the device. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + active_access_code_count: int + created_at: str + max_active_access_code_count: int + message: str + warning_code: Literal["max_access_codes_reached"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + active_access_code_count=d.get("active_access_code_count", None), + created_at=d.get("created_at", None), + max_active_access_code_count=d.get( + "max_active_access_code_count", None + ), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + Errors = Union[ + AccountDisconnectedError, + SaltoKsSubscriptionLimitExceededError, + InsufficientPermissionsError, + DormakabaSitesDisconnectedError, + DeviceOfflineError, + DeviceRemovedError, + HubDisconnectedError, + DeviceDisconnectedError, + EmptyBackupAccessCodePoolError, + AugustLockNotAuthorizedError, + MissingDeviceCredentialsError, + AuxiliaryHeatRunningError, + SubscriptionRequiredError, + BridgeDisconnectedError, + ] + _ErrorsVariants = { + "account_disconnected": AccountDisconnectedError, + "salto_ks_subscription_limit_exceeded": SaltoKsSubscriptionLimitExceededError, + "insufficient_permissions": InsufficientPermissionsError, + "dormakaba_sites_disconnected": DormakabaSitesDisconnectedError, + "device_offline": DeviceOfflineError, + "device_removed": DeviceRemovedError, + "hub_disconnected": HubDisconnectedError, + "device_disconnected": DeviceDisconnectedError, + "empty_backup_access_code_pool": EmptyBackupAccessCodePoolError, + "august_lock_not_authorized": AugustLockNotAuthorizedError, + "missing_device_credentials": MissingDeviceCredentialsError, + "auxiliary_heat_running": AuxiliaryHeatRunningError, + "subscription_required": SubscriptionRequiredError, + "bridge_disconnected": BridgeDisconnectedError, + } + + Warnings = Union[ + PartialBackupAccessCodePoolWarning, + ManyActiveBackupCodesWarning, + ThirdPartyIntegrationDetectedWarning, + TtlockLockGatewayUnlockingNotEnabledWarning, + TtlockWeakGatewaySignalWarning, + PowerSavingModeWarning, + TemperatureThresholdExceededWarning, + DeviceCommunicationDegradedWarning, + ScheduledMaintenanceWindowWarning, + DeviceHasFlakyConnectionWarning, + SaltoKsOfficeModeWarning, + SaltoKsPrivacyModeWarning, + PrivacyModeWarning, + SaltoKsSubscriptionLimitAlmostReachedWarning, + SaltoKsLockAccessCodeSupportRemovedWarning, + UnknownIssueWithPhoneWarning, + LocklyTimeZoneNotConfiguredWarning, + UltraloqTimeZoneUnknownWarning, + TimeZoneUnknownWarning, + TimeZoneMismatchWarning, + TwoNDeviceMissingTimezoneWarning, + HubRequiredForAdditionalCapabilitiesWarning, + ProviderIssueWarning, + KeynestUnsupportedLockerWarning, + AccessoryKeypadSetupRequiredWarning, + UnreliableOnlineStatusWarning, + MaxAccessCodesReachedWarning, + ] + _WarningsVariants = { + "partial_backup_access_code_pool": PartialBackupAccessCodePoolWarning, + "many_active_backup_codes": ManyActiveBackupCodesWarning, + "third_party_integration_detected": ThirdPartyIntegrationDetectedWarning, + "ttlock_lock_gateway_unlocking_not_enabled": TtlockLockGatewayUnlockingNotEnabledWarning, + "ttlock_weak_gateway_signal": TtlockWeakGatewaySignalWarning, + "power_saving_mode": PowerSavingModeWarning, + "temperature_threshold_exceeded": TemperatureThresholdExceededWarning, + "device_communication_degraded": DeviceCommunicationDegradedWarning, + "scheduled_maintenance_window": ScheduledMaintenanceWindowWarning, + "device_has_flaky_connection": DeviceHasFlakyConnectionWarning, + "salto_ks_office_mode": SaltoKsOfficeModeWarning, + "salto_ks_privacy_mode": SaltoKsPrivacyModeWarning, + "privacy_mode": PrivacyModeWarning, + "salto_ks_subscription_limit_almost_reached": SaltoKsSubscriptionLimitAlmostReachedWarning, + "salto_ks_lock_access_code_support_removed": SaltoKsLockAccessCodeSupportRemovedWarning, + "unknown_issue_with_phone": UnknownIssueWithPhoneWarning, + "lockly_time_zone_not_configured": LocklyTimeZoneNotConfiguredWarning, + "ultraloq_time_zone_unknown": UltraloqTimeZoneUnknownWarning, + "time_zone_unknown": TimeZoneUnknownWarning, + "time_zone_mismatch": TimeZoneMismatchWarning, + "two_n_device_missing_timezone": TwoNDeviceMissingTimezoneWarning, + "hub_required_for_additional_capabilities": HubRequiredForAdditionalCapabilitiesWarning, + "provider_issue": ProviderIssueWarning, + "keynest_unsupported_locker": KeynestUnsupportedLockerWarning, + "accessory_keypad_setup_required": AccessoryKeypadSetupRequiredWarning, + "unreliable_online_status": UnreliableOnlineStatusWarning, + "max_access_codes_reached": MaxAccessCodesReachedWarning, + } + can_configure_auto_lock: Optional[bool] can_hvac_cool: Optional[bool] can_hvac_heat: Optional[bool] @@ -360,12 +1420,60 @@ def from_dict(cls, d: Any): can_simulate_removal: Optional[bool] can_turn_off_hvac: Optional[bool] can_unlock_with_code: Optional[bool] - capabilities_supported: List[str] + capabilities_supported: List[ + Literal[ + "access_code", "lock", "noise_detection", "thermostat", "battery", "phone" + ] + ] connected_account_id: str created_at: str custom_metadata: Dict[str, Union[str, bool]] device_id: str - device_type: str + device_type: Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] errors: List[Errors] is_managed: Literal[False] location: Optional[Location] @@ -416,7 +1524,10 @@ def from_dict(cls, d: Any): custom_metadata=DeepAttrDict(d.get("custom_metadata", None)), device_id=d.get("device_id", None), device_type=d.get("device_type", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], is_managed=d.get("is_managed", None), location=( cls.Location.from_dict(d.get("location")) @@ -428,6 +1539,9 @@ def from_dict(cls, d: Any): if d.get("properties") is not None else None ), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/unmanaged_user_identity.py b/seam/resources/unmanaged_user_identity.py index 657c526c..e17be7ef 100644 --- a/seam/resources/unmanaged_user_identity.py +++ b/seam/resources/unmanaged_user_identity.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class UnmanagedUserIdentity: """Represents an unmanaged user identity. Unmanaged user identities do not have keys. @@ -29,8 +36,8 @@ class UnmanagedUserIdentity: :ivar workspace_id: ID of the workspace that contains the user identity.""" @dataclass - class Errors(ResourceMapping): - """Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + class IssueWithAcsUserError(ResourceMapping): + """Indicates that there is an issue with an access system user associated with this user identity. :ivar acs_system_id: ID of the access system that the user identity is associated with. @@ -46,7 +53,7 @@ class Errors(ResourceMapping): acs_system_id: str acs_user_id: str created_at: str - error_code: str + error_code: Literal["issue_with_acs_user"] message: str @classmethod @@ -60,8 +67,31 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + class BeingDeletedWarning(ResourceMapping): + """Indicates that the user identity is currently being deleted. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class AcsUserProfileDoesNotMatchUserIdentityWarning(ResourceMapping): + """Indicates that the ACS user's profile does not match the user identity's profile :ivar created_at: Date and time at which Seam created the warning. @@ -72,7 +102,7 @@ class Warnings(ResourceMapping): created_at: str message: str - warning_code: str + warning_code: Literal["acs_user_profile_does_not_match_user_identity"] @classmethod def from_dict(cls, d: Any): @@ -82,6 +112,17 @@ def from_dict(cls, d: Any): warning_code=d.get("warning_code", None), ) + Errors = Union[IssueWithAcsUserError] + _ErrorsVariants = { + "issue_with_acs_user": IssueWithAcsUserError, + } + + Warnings = Union[BeingDeletedWarning, AcsUserProfileDoesNotMatchUserIdentityWarning] + _WarningsVariants = { + "being_deleted": BeingDeletedWarning, + "acs_user_profile_does_not_match_user_identity": AcsUserProfileDoesNotMatchUserIdentityWarning, + } + acs_user_ids: List[str] created_at: str display_name: str @@ -100,10 +141,16 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), display_name=d.get("display_name", None), email_address=d.get("email_address", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], full_name=d.get("full_name", None), phone_number=d.get("phone_number", None), user_identity_id=d.get("user_identity_id", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/user_identity.py b/seam/resources/user_identity.py index 8cf8e309..04718d2c 100644 --- a/seam/resources/user_identity.py +++ b/seam/resources/user_identity.py @@ -4,6 +4,13 @@ from ..resource_mapping import ResourceMapping +def _from_discriminated_dict( + d: Any, variants: Dict[str, Any], discriminator: str +) -> Any: + variant = variants.get(d.get(discriminator)) + return DeepAttrDict(d) if variant is None else variant.from_dict(d) + + @dataclass class UserIdentity: """Represents a `user identity `_ associated with an application user account. @@ -31,8 +38,8 @@ class UserIdentity: :ivar workspace_id: ID of the workspace that contains the user identity.""" @dataclass - class Errors(ResourceMapping): - """Array of errors associated with the user identity. Each error object within the array contains fields like "error_code" and "message." "error_code" is a string that uniquely identifies the type of error, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the error, offering insights into the issue and potentially how to rectify it. + class IssueWithAcsUserError(ResourceMapping): + """Indicates that there is an issue with an access system user associated with this user identity. :ivar acs_system_id: ID of the access system that the user identity is associated with. @@ -48,7 +55,7 @@ class Errors(ResourceMapping): acs_system_id: str acs_user_id: str created_at: str - error_code: str + error_code: Literal["issue_with_acs_user"] message: str @classmethod @@ -62,8 +69,31 @@ def from_dict(cls, d: Any): ) @dataclass - class Warnings(ResourceMapping): - """Array of warnings associated with the user identity. Each warning object within the array contains two fields: "warning_code" and "message." "warning_code" is a string that uniquely identifies the type of warning, enabling quick recognition and categorization of the issue. "message" provides a more detailed description of the warning, offering insights into the issue and potentially how to rectify it. + class BeingDeletedWarning(ResourceMapping): + """Indicates that the user identity is currently being deleted. + + :ivar created_at: Date and time at which Seam created the warning. + + :ivar message: Detailed description of the warning. Provides insights into the issue and potentially how to rectify it. + + :ivar warning_code: Unique identifier of the type of warning. Enables quick recognition and categorization of the issue. + """ + + created_at: str + message: str + warning_code: Literal["being_deleted"] + + @classmethod + def from_dict(cls, d: Any): + return cls( + created_at=d.get("created_at", None), + message=d.get("message", None), + warning_code=d.get("warning_code", None), + ) + + @dataclass + class AcsUserProfileDoesNotMatchUserIdentityWarning(ResourceMapping): + """Indicates that the ACS user's profile does not match the user identity's profile :ivar created_at: Date and time at which Seam created the warning. @@ -74,7 +104,7 @@ class Warnings(ResourceMapping): created_at: str message: str - warning_code: str + warning_code: Literal["acs_user_profile_does_not_match_user_identity"] @classmethod def from_dict(cls, d: Any): @@ -84,6 +114,17 @@ def from_dict(cls, d: Any): warning_code=d.get("warning_code", None), ) + Errors = Union[IssueWithAcsUserError] + _ErrorsVariants = { + "issue_with_acs_user": IssueWithAcsUserError, + } + + Warnings = Union[BeingDeletedWarning, AcsUserProfileDoesNotMatchUserIdentityWarning] + _WarningsVariants = { + "being_deleted": BeingDeletedWarning, + "acs_user_profile_does_not_match_user_identity": AcsUserProfileDoesNotMatchUserIdentityWarning, + } + acs_user_ids: List[str] created_at: str display_name: str @@ -103,11 +144,17 @@ def from_dict(cls, d: Any): created_at=d.get("created_at", None), display_name=d.get("display_name", None), email_address=d.get("email_address", None), - errors=[cls.Errors.from_dict(i) for i in d.get("errors") or []], + errors=[ + _from_discriminated_dict(i, cls._ErrorsVariants, "error_code") + for i in d.get("errors") or [] + ], full_name=d.get("full_name", None), phone_number=d.get("phone_number", None), user_identity_id=d.get("user_identity_id", None), user_identity_key=d.get("user_identity_key", None), - warnings=[cls.Warnings.from_dict(i) for i in d.get("warnings") or []], + warnings=[ + _from_discriminated_dict(i, cls._WarningsVariants, "warning_code") + for i in d.get("warnings") or [] + ], workspace_id=d.get("workspace_id", None), ) diff --git a/seam/resources/workspace.py b/seam/resources/workspace.py index dc97140d..6e9616ec 100644 --- a/seam/resources/workspace.py +++ b/seam/resources/workspace.py @@ -44,7 +44,7 @@ class ConnectWebviewCustomization(ResourceMapping): """ inviter_logo_url: Optional[str] - logo_shape: Optional[str] + logo_shape: Optional[Literal["circle", "square"]] primary_button_color: Optional[str] primary_button_text_color: Optional[str] success_message: Optional[str] diff --git a/seam/routes/access_codes.py b/seam/routes/access_codes.py index c34fe2ca..c8cf0b2f 100644 --- a/seam/routes/access_codes.py +++ b/seam/routes/access_codes.py @@ -43,7 +43,7 @@ def create( is_external_modification_allowed: Optional[bool] = None, is_offline_access_code: Optional[bool] = None, is_one_time_use: Optional[bool] = None, - max_time_rounding: Optional[str] = None, + max_time_rounding: Optional[Literal["1hour", "1day", "1h", "1d"]] = None, name: Optional[str] = None, prefer_native_scheduling: Optional[bool] = None, preferred_code_length: Optional[float] = None, @@ -103,7 +103,9 @@ def create_multiple( device_ids: List[str], allow_external_modification: Optional[bool] = None, attempt_for_offline_device: Optional[bool] = None, - behavior_when_code_cannot_be_shared: Optional[str] = None, + behavior_when_code_cannot_be_shared: Optional[ + Literal["throw", "create_random_code"] + ] = None, code: Optional[str] = None, ends_at: Optional[str] = None, is_external_modification_allowed: Optional[bool] = None, @@ -306,7 +308,7 @@ def update( is_managed: Optional[bool] = None, name: Optional[str] = None, starts_at: Optional[str] = None, - type: Optional[str] = None, + type: Optional[Literal["ongoing", "time_bound"]] = None, ) -> None: """Updates a specified active or upcoming `access code `_. @@ -401,7 +403,7 @@ async def create( is_external_modification_allowed: Optional[bool] = None, is_offline_access_code: Optional[bool] = None, is_one_time_use: Optional[bool] = None, - max_time_rounding: Optional[str] = None, + max_time_rounding: Optional[Literal["1hour", "1day", "1h", "1d"]] = None, name: Optional[str] = None, prefer_native_scheduling: Optional[bool] = None, preferred_code_length: Optional[float] = None, @@ -461,7 +463,9 @@ async def create_multiple( device_ids: List[str], allow_external_modification: Optional[bool] = None, attempt_for_offline_device: Optional[bool] = None, - behavior_when_code_cannot_be_shared: Optional[str] = None, + behavior_when_code_cannot_be_shared: Optional[ + Literal["throw", "create_random_code"] + ] = None, code: Optional[str] = None, ends_at: Optional[str] = None, is_external_modification_allowed: Optional[bool] = None, @@ -666,7 +670,7 @@ async def update( is_managed: Optional[bool] = None, name: Optional[str] = None, starts_at: Optional[str] = None, - type: Optional[str] = None, + type: Optional[Literal["ongoing", "time_bound"]] = None, ) -> None: """Updates a specified active or upcoming `access code `_. @@ -766,7 +770,7 @@ def create( is_external_modification_allowed: Optional[bool] = None, is_offline_access_code: Optional[bool] = None, is_one_time_use: Optional[bool] = None, - max_time_rounding: Optional[str] = None, + max_time_rounding: Optional[Literal["1hour", "1day", "1h", "1d"]] = None, name: Optional[str] = None, prefer_native_scheduling: Optional[bool] = None, preferred_code_length: Optional[float] = None, @@ -874,7 +878,9 @@ def create_multiple( device_ids: List[str], allow_external_modification: Optional[bool] = None, attempt_for_offline_device: Optional[bool] = None, - behavior_when_code_cannot_be_shared: Optional[str] = None, + behavior_when_code_cannot_be_shared: Optional[ + Literal["throw", "create_random_code"] + ] = None, code: Optional[str] = None, ends_at: Optional[str] = None, is_external_modification_allowed: Optional[bool] = None, @@ -1237,7 +1243,7 @@ def update( is_managed: Optional[bool] = None, name: Optional[str] = None, starts_at: Optional[str] = None, - type: Optional[str] = None, + type: Optional[Literal["ongoing", "time_bound"]] = None, ) -> None: """Updates a specified active or upcoming `access code `_. @@ -1393,7 +1399,7 @@ async def create( is_external_modification_allowed: Optional[bool] = None, is_offline_access_code: Optional[bool] = None, is_one_time_use: Optional[bool] = None, - max_time_rounding: Optional[str] = None, + max_time_rounding: Optional[Literal["1hour", "1day", "1h", "1d"]] = None, name: Optional[str] = None, prefer_native_scheduling: Optional[bool] = None, preferred_code_length: Optional[float] = None, @@ -1501,7 +1507,9 @@ async def create_multiple( device_ids: List[str], allow_external_modification: Optional[bool] = None, attempt_for_offline_device: Optional[bool] = None, - behavior_when_code_cannot_be_shared: Optional[str] = None, + behavior_when_code_cannot_be_shared: Optional[ + Literal["throw", "create_random_code"] + ] = None, code: Optional[str] = None, ends_at: Optional[str] = None, is_external_modification_allowed: Optional[bool] = None, @@ -1868,7 +1876,7 @@ async def update( is_managed: Optional[bool] = None, name: Optional[str] = None, starts_at: Optional[str] = None, - type: Optional[str] = None, + type: Optional[Literal["ongoing", "time_bound"]] = None, ) -> None: """Updates a specified active or upcoming `access code `_. diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py index 9f78d9fd..525fff80 100644 --- a/seam/routes/access_grants.py +++ b/seam/routes/access_grants.py @@ -109,8 +109,34 @@ def get_related( *, access_grant_ids: Optional[List[str]] = None, access_grant_keys: Optional[List[str]] = None, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Grants. @@ -314,8 +340,34 @@ async def get_related( *, access_grant_ids: Optional[List[str]] = None, access_grant_keys: Optional[List[str]] = None, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Grants. @@ -598,8 +650,34 @@ def get_related( *, access_grant_ids: Optional[List[str]] = None, access_grant_keys: Optional[List[str]] = None, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Grants. @@ -975,8 +1053,34 @@ async def get_related( *, access_grant_ids: Optional[List[str]] = None, access_grant_keys: Optional[List[str]] = None, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "user_identities", + "acs_access_groups", + "access_methods", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Grants. diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py index 3ed49f0c..c8e9cc2a 100644 --- a/seam/routes/access_methods.py +++ b/seam/routes/access_methods.py @@ -3,7 +3,7 @@ from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata from ..null import Null -from ..resources import ActionAttempt, AccessMethod, Batch +from ..resources import ActionAttempt, AccessMethod, Batch, action_attempt_from_dict from .access_methods_unmanaged import ( AbstractAccessMethodsUnmanaged, AccessMethodsUnmanaged, @@ -100,8 +100,34 @@ def get_related( self, *, access_method_ids: List[str], - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Methods. @@ -258,8 +284,34 @@ async def get_related( self, *, access_method_ids: List[str], - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Methods. @@ -387,7 +439,7 @@ def assign_card( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -475,7 +527,7 @@ def encode( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -513,8 +565,34 @@ def get_related( self, *, access_method_ids: List[str], - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Methods. @@ -654,7 +732,7 @@ def unlock_door( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -714,7 +792,7 @@ async def assign_card( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -802,7 +880,7 @@ async def encode( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -840,8 +918,34 @@ async def get_related( self, *, access_method_ids: List[str], - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "access_grants", + "access_methods", + "instant_keys", + "client_sessions", + "acs_credentials", + ] + ] + ] = None, ) -> Batch: """Gets all related resources for one or more Access Methods. @@ -981,6 +1085,6 @@ async def unlock_door( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/routes/acs_credentials.py b/seam/routes/acs_credentials.py index 7c02a486..c515485f 100644 --- a/seam/routes/acs_credentials.py +++ b/seam/routes/acs_credentials.py @@ -31,7 +31,7 @@ def assign( def create( self, *, - access_method: str, + access_method: Literal["code", "card", "mobile_key", "cloud_key"], acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, allowed_acs_entrance_ids: Optional[List[str]] = None, @@ -207,7 +207,7 @@ async def assign( async def create( self, *, - access_method: str, + access_method: Literal["code", "card", "mobile_key", "cloud_key"], acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, allowed_acs_entrance_ids: Optional[List[str]] = None, @@ -412,7 +412,7 @@ def assign( def create( self, *, - access_method: str, + access_method: Literal["code", "card", "mobile_key", "cloud_key"], acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, allowed_acs_entrance_ids: Optional[List[str]] = None, @@ -766,7 +766,7 @@ async def assign( async def create( self, *, - access_method: str, + access_method: Literal["code", "card", "mobile_key", "cloud_key"], acs_system_id: Optional[str] = None, acs_user_id: Optional[str] = None, allowed_acs_entrance_ids: Optional[List[str]] = None, diff --git a/seam/routes/acs_encoders.py b/seam/routes/acs_encoders.py index de351d14..47d76d8f 100644 --- a/seam/routes/acs_encoders.py +++ b/seam/routes/acs_encoders.py @@ -3,7 +3,7 @@ from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata from ..null import Null -from ..resources import ActionAttempt, AcsEncoder +from ..resources import ActionAttempt, AcsEncoder, action_attempt_from_dict from .acs_encoders_simulate import ( AbstractAcsEncodersSimulate, AcsEncodersSimulate, @@ -308,7 +308,7 @@ def encode_credential( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -422,7 +422,7 @@ def scan_credential( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -483,7 +483,7 @@ def scan_to_assign_credential( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -550,7 +550,7 @@ async def encode_credential( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -664,7 +664,7 @@ async def scan_credential( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -725,6 +725,6 @@ async def scan_to_assign_credential( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/routes/acs_encoders_simulate.py b/seam/routes/acs_encoders_simulate.py index 7f45007c..9739794a 100644 --- a/seam/routes/acs_encoders_simulate.py +++ b/seam/routes/acs_encoders_simulate.py @@ -11,7 +11,14 @@ def next_credential_encode_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "encoding_interrupted", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id: Optional[str] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -27,7 +34,10 @@ def next_credential_encode_will_fail( @abc.abstractmethod def next_credential_encode_will_succeed( - self, *, acs_encoder_id: str, scenario: Optional[str] = None + self, + *, + acs_encoder_id: str, + scenario: Optional[Literal["credential_is_issued"]] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -43,7 +53,13 @@ def next_credential_scan_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id_on_seam: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -63,7 +79,14 @@ def next_credential_scan_will_succeed( *, acs_encoder_id: str, acs_credential_id_on_seam: Optional[str] = None, - scenario: Optional[str] = None, + scenario: Optional[ + Literal[ + "credential_exists_on_seam", + "credential_on_encoder_needs_update", + "credential_does_not_exist_on_seam", + "credential_on_encoder_is_empty", + ] + ] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -84,7 +107,14 @@ async def next_credential_encode_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "encoding_interrupted", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id: Optional[str] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -100,7 +130,10 @@ async def next_credential_encode_will_fail( @abc.abstractmethod async def next_credential_encode_will_succeed( - self, *, acs_encoder_id: str, scenario: Optional[str] = None + self, + *, + acs_encoder_id: str, + scenario: Optional[Literal["credential_is_issued"]] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -116,7 +149,13 @@ async def next_credential_scan_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id_on_seam: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -136,7 +175,14 @@ async def next_credential_scan_will_succeed( *, acs_encoder_id: str, acs_credential_id_on_seam: Optional[str] = None, - scenario: Optional[str] = None, + scenario: Optional[ + Literal[ + "credential_exists_on_seam", + "credential_on_encoder_needs_update", + "credential_does_not_exist_on_seam", + "credential_on_encoder_is_empty", + ] + ] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -164,7 +210,14 @@ def next_credential_encode_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "encoding_interrupted", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id: Optional[str] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -202,7 +255,10 @@ def next_credential_encode_will_fail( has_pagination=False, ) def next_credential_encode_will_succeed( - self, *, acs_encoder_id: str, scenario: Optional[str] = None + self, + *, + acs_encoder_id: str, + scenario: Optional[Literal["credential_is_issued"]] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -239,7 +295,13 @@ def next_credential_scan_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id_on_seam: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -281,7 +343,14 @@ def next_credential_scan_will_succeed( *, acs_encoder_id: str, acs_credential_id_on_seam: Optional[str] = None, - scenario: Optional[str] = None, + scenario: Optional[ + Literal[ + "credential_exists_on_seam", + "credential_on_encoder_needs_update", + "credential_does_not_exist_on_seam", + "credential_on_encoder_is_empty", + ] + ] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -328,7 +397,14 @@ async def next_credential_encode_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "encoding_interrupted", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id: Optional[str] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -366,7 +442,10 @@ async def next_credential_encode_will_fail( has_pagination=False, ) async def next_credential_encode_will_succeed( - self, *, acs_encoder_id: str, scenario: Optional[str] = None + self, + *, + acs_encoder_id: str, + scenario: Optional[Literal["credential_is_issued"]] = None, ) -> None: """Simulates that the next attempt to encode a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. @@ -403,7 +482,13 @@ async def next_credential_scan_will_fail( self, *, acs_encoder_id: str, - error_code: Optional[str] = None, + error_code: Optional[ + Literal[ + "no_credential_on_encoder", + "uncategorized_error", + "action_attempt_expired", + ] + ] = None, acs_credential_id_on_seam: Optional[str] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will fail. You can only perform this action within a `sandbox workspace `_. @@ -445,7 +530,14 @@ async def next_credential_scan_will_succeed( *, acs_encoder_id: str, acs_credential_id_on_seam: Optional[str] = None, - scenario: Optional[str] = None, + scenario: Optional[ + Literal[ + "credential_exists_on_seam", + "credential_on_encoder_needs_update", + "credential_does_not_exist_on_seam", + "credential_on_encoder_is_empty", + ] + ] = None, ) -> None: """Simulates that the next attempt to scan a `credential `_ using the specified `encoder `_ will succeed. You can only perform this action within a `sandbox workspace `_. diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 3198eeb8..173b1491 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -3,7 +3,12 @@ from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata from ..null import Null -from ..resources import AcsEntrance, AcsCredential, ActionAttempt +from ..resources import ( + AcsEntrance, + AcsCredential, + ActionAttempt, + action_attempt_from_dict, +) from ..modules.action_attempts import ( resolve_action_attempt, resolve_action_attempt_async, @@ -87,7 +92,10 @@ def list( @abc.abstractmethod def list_credentials_with_access( - self, *, acs_entrance_id: str, include_if: Optional[List[str]] = None + self, + *, + acs_entrance_id: str, + include_if: Optional[List[Literal["visionline_metadata.is_valid"]]] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_ with access to a specified `entrance `_. @@ -199,7 +207,10 @@ async def list( @abc.abstractmethod async def list_credentials_with_access( - self, *, acs_entrance_id: str, include_if: Optional[List[str]] = None + self, + *, + acs_entrance_id: str, + include_if: Optional[List[Literal["visionline_metadata.is_valid"]]] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_ with access to a specified `entrance `_. @@ -381,7 +392,10 @@ def list( has_pagination=False, ) def list_credentials_with_access( - self, *, acs_entrance_id: str, include_if: Optional[List[str]] = None + self, + *, + acs_entrance_id: str, + include_if: Optional[List[Literal["visionline_metadata.is_valid"]]] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_ with access to a specified `entrance `_. @@ -453,7 +467,7 @@ def unlock( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -605,7 +619,10 @@ async def list( has_pagination=False, ) async def list_credentials_with_access( - self, *, acs_entrance_id: str, include_if: Optional[List[str]] = None + self, + *, + acs_entrance_id: str, + include_if: Optional[List[Literal["visionline_metadata.is_valid"]]] = None, ) -> List[AcsCredential]: """Returns a list of all `credentials `_ with access to a specified `entrance `_. @@ -677,6 +694,6 @@ async def unlock( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/routes/action_attempts.py b/seam/routes/action_attempts.py index 12a65a44..771f6b08 100644 --- a/seam/routes/action_attempts.py +++ b/seam/routes/action_attempts.py @@ -3,7 +3,7 @@ from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata from ..null import Null -from ..resources import ActionAttempt +from ..resources import ActionAttempt, action_attempt_from_dict from ..modules.action_attempts import ( resolve_action_attempt, resolve_action_attempt_async, @@ -139,7 +139,7 @@ def get( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -178,7 +178,7 @@ def list( res = self.client.get("/action_attempts/list", params=params) - return [ActionAttempt.from_dict(item) for item in res["action_attempts"]] + return [action_attempt_from_dict(item) for item in res["action_attempts"]] class AsyncActionAttempts(AbstractAsyncActionAttempts): @@ -224,7 +224,7 @@ async def get( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -263,4 +263,4 @@ async def list( res = await self.client.get("/action_attempts/list", params=params) - return [ActionAttempt.from_dict(item) for item in res["action_attempts"]] + return [action_attempt_from_dict(item) for item in res["action_attempts"]] diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index c9a95b70..44588436 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -12,15 +12,106 @@ class AbstractConnectWebviews(abc.ABC): def create( self, *, - accepted_capabilities: Optional[List[str]] = None, - accepted_providers: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, + accepted_providers: Optional[ + List[ + Literal[ + "hotek", + "dormakaba_community", + "legic_connect", + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "schlage", + "smartthings", + "yale", + "genie", + "doorking", + "salto", + "salto_ks", + "salto_ks_accept", + "lockly", + "ttlock", + "linear", + "noiseaware", + "nuki", + "igloo", + "kwikset", + "minut", + "my_2n", + "controlbyweb", + "nest", + "igloohome", + "ecobee", + "four_suites", + "dormakaba_oracode", + "pti", + "wyze", + "seam_passport", + "visionline", + "assa_abloy_credential_service", + "tedee", + "honeywell_resideo", + "first_alert", + "latch", + "akiles", + "assa_abloy_vostio", + "assa_abloy_vostio_credential_service", + "tado", + "salto_space", + "sensi", + "keynest", + "korelock", + "keyincode", + "dormakaba_ambiance", + "ultraloq", + "yacan", + "dusaw", + "sifely", + "thirty_three_lock", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "aqara", + "yale_access", + "hid_cm", + "google_nest", + "slack", + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, custom_redirect_failure_url: Optional[str] = None, custom_redirect_url: Optional[str] = None, customer_key: Optional[str] = None, excluded_providers: Optional[List[str]] = None, - provider_category: Optional[str] = None, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + "internal_beta", + ] + ] = None, wait_for_device_creation: Optional[bool] = None, ) -> ConnectWebview: """Creates a new `Connect Webview `_. @@ -113,15 +204,106 @@ class AbstractAsyncConnectWebviews(abc.ABC): async def create( self, *, - accepted_capabilities: Optional[List[str]] = None, - accepted_providers: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, + accepted_providers: Optional[ + List[ + Literal[ + "hotek", + "dormakaba_community", + "legic_connect", + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "schlage", + "smartthings", + "yale", + "genie", + "doorking", + "salto", + "salto_ks", + "salto_ks_accept", + "lockly", + "ttlock", + "linear", + "noiseaware", + "nuki", + "igloo", + "kwikset", + "minut", + "my_2n", + "controlbyweb", + "nest", + "igloohome", + "ecobee", + "four_suites", + "dormakaba_oracode", + "pti", + "wyze", + "seam_passport", + "visionline", + "assa_abloy_credential_service", + "tedee", + "honeywell_resideo", + "first_alert", + "latch", + "akiles", + "assa_abloy_vostio", + "assa_abloy_vostio_credential_service", + "tado", + "salto_space", + "sensi", + "keynest", + "korelock", + "keyincode", + "dormakaba_ambiance", + "ultraloq", + "yacan", + "dusaw", + "sifely", + "thirty_three_lock", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "aqara", + "yale_access", + "hid_cm", + "google_nest", + "slack", + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, custom_redirect_failure_url: Optional[str] = None, custom_redirect_url: Optional[str] = None, customer_key: Optional[str] = None, excluded_providers: Optional[List[str]] = None, - provider_category: Optional[str] = None, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + "internal_beta", + ] + ] = None, wait_for_device_creation: Optional[bool] = None, ) -> ConnectWebview: """Creates a new `Connect Webview `_. @@ -221,15 +403,106 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def create( self, *, - accepted_capabilities: Optional[List[str]] = None, - accepted_providers: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, + accepted_providers: Optional[ + List[ + Literal[ + "hotek", + "dormakaba_community", + "legic_connect", + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "schlage", + "smartthings", + "yale", + "genie", + "doorking", + "salto", + "salto_ks", + "salto_ks_accept", + "lockly", + "ttlock", + "linear", + "noiseaware", + "nuki", + "igloo", + "kwikset", + "minut", + "my_2n", + "controlbyweb", + "nest", + "igloohome", + "ecobee", + "four_suites", + "dormakaba_oracode", + "pti", + "wyze", + "seam_passport", + "visionline", + "assa_abloy_credential_service", + "tedee", + "honeywell_resideo", + "first_alert", + "latch", + "akiles", + "assa_abloy_vostio", + "assa_abloy_vostio_credential_service", + "tado", + "salto_space", + "sensi", + "keynest", + "korelock", + "keyincode", + "dormakaba_ambiance", + "ultraloq", + "yacan", + "dusaw", + "sifely", + "thirty_three_lock", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "aqara", + "yale_access", + "hid_cm", + "google_nest", + "slack", + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, custom_redirect_failure_url: Optional[str] = None, custom_redirect_url: Optional[str] = None, customer_key: Optional[str] = None, excluded_providers: Optional[List[str]] = None, - provider_category: Optional[str] = None, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + "internal_beta", + ] + ] = None, wait_for_device_creation: Optional[bool] = None, ) -> ConnectWebview: """Creates a new `Connect Webview `_. @@ -407,15 +680,106 @@ def __init__(self, client: AsyncSeamHttpClient, defaults: Dict[str, Any]): async def create( self, *, - accepted_capabilities: Optional[List[str]] = None, - accepted_providers: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, + accepted_providers: Optional[ + List[ + Literal[ + "hotek", + "dormakaba_community", + "legic_connect", + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "schlage", + "smartthings", + "yale", + "genie", + "doorking", + "salto", + "salto_ks", + "salto_ks_accept", + "lockly", + "ttlock", + "linear", + "noiseaware", + "nuki", + "igloo", + "kwikset", + "minut", + "my_2n", + "controlbyweb", + "nest", + "igloohome", + "ecobee", + "four_suites", + "dormakaba_oracode", + "pti", + "wyze", + "seam_passport", + "visionline", + "assa_abloy_credential_service", + "tedee", + "honeywell_resideo", + "first_alert", + "latch", + "akiles", + "assa_abloy_vostio", + "assa_abloy_vostio_credential_service", + "tado", + "salto_space", + "sensi", + "keynest", + "korelock", + "keyincode", + "dormakaba_ambiance", + "ultraloq", + "yacan", + "dusaw", + "sifely", + "thirty_three_lock", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "aqara", + "yale_access", + "hid_cm", + "google_nest", + "slack", + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, custom_redirect_failure_url: Optional[str] = None, custom_redirect_url: Optional[str] = None, customer_key: Optional[str] = None, excluded_providers: Optional[List[str]] = None, - provider_category: Optional[str] = None, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + "internal_beta", + ] + ] = None, wait_for_device_creation: Optional[bool] = None, ) -> ConnectWebview: """Creates a new `Connect Webview `_. diff --git a/seam/routes/connected_accounts.py b/seam/routes/connected_accounts.py index bfd15fe8..4856a685 100644 --- a/seam/routes/connected_accounts.py +++ b/seam/routes/connected_accounts.py @@ -92,7 +92,13 @@ def update( self, *, connected_account_id: str, - accepted_capabilities: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, @@ -196,7 +202,13 @@ async def update( self, *, connected_account_id: str, - accepted_capabilities: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, @@ -380,7 +392,13 @@ def update( self, *, connected_account_id: str, - accepted_capabilities: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, @@ -590,7 +608,13 @@ async def update( self, *, connected_account_id: str, - accepted_capabilities: Optional[List[str]] = None, + accepted_capabilities: Optional[ + List[ + Literal[ + "lock", "thermostat", "noise_sensor", "access_control", "camera" + ] + ] + ] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, diff --git a/seam/routes/customers.py b/seam/routes/customers.py index b848ebcb..2877a35c 100644 --- a/seam/routes/customers.py +++ b/seam/routes/customers.py @@ -18,8 +18,21 @@ def create_portal( features: Optional[Dict[str, Any]] = None, is_embedded: Optional[bool] = None, landing_page: Optional[Dict[str, Any]] = None, - locale: Optional[str] = None, - navigation_mode: Optional[str] = None, + locale: Optional[ + Literal[ + "en-US", + "pt-PT", + "fr-FR", + "it-IT", + "es-ES", + "de-DE", + "nl-NL", + "el-GR", + "pl-PL", + "ru-RU", + ] + ] = None, + navigation_mode: Optional[Literal["full", "restricted"]] = None, read_only: Optional[bool] = None, customer_data: Optional[Dict[str, Any]] = None, ) -> CustomerPortal: @@ -200,8 +213,21 @@ async def create_portal( features: Optional[Dict[str, Any]] = None, is_embedded: Optional[bool] = None, landing_page: Optional[Dict[str, Any]] = None, - locale: Optional[str] = None, - navigation_mode: Optional[str] = None, + locale: Optional[ + Literal[ + "en-US", + "pt-PT", + "fr-FR", + "it-IT", + "es-ES", + "de-DE", + "nl-NL", + "el-GR", + "pl-PL", + "ru-RU", + ] + ] = None, + navigation_mode: Optional[Literal["full", "restricted"]] = None, read_only: Optional[bool] = None, customer_data: Optional[Dict[str, Any]] = None, ) -> CustomerPortal: @@ -389,8 +415,21 @@ def create_portal( features: Optional[Dict[str, Any]] = None, is_embedded: Optional[bool] = None, landing_page: Optional[Dict[str, Any]] = None, - locale: Optional[str] = None, - navigation_mode: Optional[str] = None, + locale: Optional[ + Literal[ + "en-US", + "pt-PT", + "fr-FR", + "it-IT", + "es-ES", + "de-DE", + "nl-NL", + "el-GR", + "pl-PL", + "ru-RU", + ] + ] = None, + navigation_mode: Optional[Literal["full", "restricted"]] = None, read_only: Optional[bool] = None, customer_data: Optional[Dict[str, Any]] = None, ) -> CustomerPortal: @@ -704,8 +743,21 @@ async def create_portal( features: Optional[Dict[str, Any]] = None, is_embedded: Optional[bool] = None, landing_page: Optional[Dict[str, Any]] = None, - locale: Optional[str] = None, - navigation_mode: Optional[str] = None, + locale: Optional[ + Literal[ + "en-US", + "pt-PT", + "fr-FR", + "it-IT", + "es-ES", + "de-DE", + "nl-NL", + "el-GR", + "pl-PL", + "ru-RU", + ] + ] = None, + navigation_mode: Optional[Literal["full", "restricted"]] = None, read_only: Optional[bool] = None, customer_data: Optional[Dict[str, Any]] = None, ) -> CustomerPortal: diff --git a/seam/routes/devices.py b/seam/routes/devices.py index cee08bf5..d0499d0a 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -58,10 +58,159 @@ def list( custom_metadata_has: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, @@ -107,7 +256,20 @@ def list( @abc.abstractmethod def list_device_providers( - self, *, provider_category: Optional[str] = None + self, + *, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + ] + ] = None, ) -> List[DeviceProvider]: """Returns a list of all device providers. @@ -200,10 +362,159 @@ async def list( custom_metadata_has: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, @@ -249,7 +560,20 @@ async def list( @abc.abstractmethod async def list_device_providers( - self, *, provider_category: Optional[str] = None + self, + *, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + ] + ] = None, ) -> List[DeviceProvider]: """Returns a list of all device providers. @@ -361,10 +685,159 @@ def list( custom_metadata_has: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, @@ -451,7 +924,20 @@ def list( has_pagination=False, ) def list_device_providers( - self, *, provider_category: Optional[str] = None + self, + *, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + ] + ] = None, ) -> List[DeviceProvider]: """Returns a list of all device providers. @@ -610,10 +1096,159 @@ async def list( custom_metadata_has: Optional[Dict[str, Union[str, bool]]] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, space_id: Optional[str] = None, @@ -700,7 +1335,20 @@ async def list( has_pagination=False, ) async def list_device_providers( - self, *, provider_category: Optional[str] = None + self, + *, + provider_category: Optional[ + Literal[ + "stable", + "consumer_smartlocks", + "beta", + "thermostats", + "noise_sensors", + "access_control_systems", + "cameras", + "connectors", + ] + ] = None, ) -> List[DeviceProvider]: """Returns a list of all device providers. diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 156f0d04..50f8d653 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -37,10 +37,159 @@ def list( created_before: Optional[str] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedDevice]: @@ -128,10 +277,159 @@ async def list( created_before: Optional[str] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedDevice]: @@ -244,10 +542,159 @@ def list( created_before: Optional[str] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedDevice]: @@ -409,10 +856,159 @@ async def list( created_before: Optional[str] = None, customer_key: Optional[str] = None, device_ids: Optional[List[str]] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + "keynest_key", + "noiseaware_activity_zone", + "minut_sensor", + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + "ios_phone", + "android_phone", + "ring_camera", + ] + ] + ] = None, limit: Optional[float] = None, - manufacturer: Optional[str] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "avigilon_alta", + "brivo", + "butterflymx", + "doorking", + "four_suites", + "genie", + "igloo", + "keywe", + "kwikset", + "linear", + "lockly", + "nuki", + "philia", + "salto", + "samsung", + "schlage", + "seam", + "unknown", + "wyze", + "yale", + "two_n", + "ttlock", + "igloohome", + "controlbyweb", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "ecobee", + "honeywell_resideo", + "keynest", + "korelock", + "minut", + "nest", + "noiseaware", + "sensi", + "smartthings", + "tado", + "ultraloq", + "ring", + "ical", + "lodgify", + "hostaway", + "guesty", + "acuity_scheduling", + "omnitec", + "kisi", + "slack", + "yacan", + ] + ] = None, page_cursor: Optional[Union[str, Null]] = None, search: Optional[str] = None, ) -> List[UnmanagedDevice]: diff --git a/seam/routes/events.py b/seam/routes/events.py index 349aa7d5..298cf0d4 100644 --- a/seam/routes/events.py +++ b/seam/routes/events.py @@ -2,7 +2,7 @@ import abc from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata -from ..resources import SeamEvent +from ..resources import SeamEvent, seam_event_from_dict class AbstractEvents(abc.ABC): @@ -52,8 +52,234 @@ def list( device_id: Optional[str] = None, device_ids: Optional[List[str]] = None, event_ids: Optional[List[str]] = None, - event_type: Optional[str] = None, - event_types: Optional[List[str]] = None, + event_type: Optional[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] = None, + event_types: Optional[ + List[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] + ] = None, limit: Optional[float] = None, since: Optional[str] = None, space_id: Optional[str] = None, @@ -172,8 +398,234 @@ async def list( device_id: Optional[str] = None, device_ids: Optional[List[str]] = None, event_ids: Optional[List[str]] = None, - event_type: Optional[str] = None, - event_types: Optional[List[str]] = None, + event_type: Optional[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] = None, + event_types: Optional[ + List[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] + ] = None, limit: Optional[float] = None, since: Optional[str] = None, space_id: Optional[str] = None, @@ -285,7 +737,7 @@ def get( res = self.client.get("/events/get", params=params) - return SeamEvent.from_dict(res["event"]) + return seam_event_from_dict(res["event"]) @route_metadata( path="/events/list", has_required_parameters=True, has_pagination=False @@ -313,8 +765,234 @@ def list( device_id: Optional[str] = None, device_ids: Optional[List[str]] = None, event_ids: Optional[List[str]] = None, - event_type: Optional[str] = None, - event_types: Optional[List[str]] = None, + event_type: Optional[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] = None, + event_types: Optional[ + List[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] + ] = None, limit: Optional[float] = None, since: Optional[str] = None, space_id: Optional[str] = None, @@ -447,7 +1125,7 @@ def list( res = self.client.get("/events/list", params=params) - return [SeamEvent.from_dict(item) for item in res["events"]] + return [seam_event_from_dict(item) for item in res["events"]] class AsyncEvents(AbstractAsyncEvents): @@ -490,7 +1168,7 @@ async def get( res = await self.client.get("/events/get", params=params) - return SeamEvent.from_dict(res["event"]) + return seam_event_from_dict(res["event"]) @route_metadata( path="/events/list", has_required_parameters=True, has_pagination=False @@ -518,8 +1196,234 @@ async def list( device_id: Optional[str] = None, device_ids: Optional[List[str]] = None, event_ids: Optional[List[str]] = None, - event_type: Optional[str] = None, - event_types: Optional[List[str]] = None, + event_type: Optional[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] = None, + event_types: Optional[ + List[ + Literal[ + "access_code.created", + "access_code.changed", + "access_code.name_changed", + "access_code.code_changed", + "access_code.time_frame_changed", + "access_code.mutations_requested", + "access_code.scheduled_on_device", + "access_code.set_on_device", + "access_code.removed_from_device", + "access_code.delay_in_setting_on_device", + "access_code.failed_to_set_on_device", + "access_code.deleted", + "access_code.delay_in_removing_from_device", + "access_code.failed_to_remove_from_device", + "access_code.modified_external_to_seam", + "access_code.deleted_external_to_seam", + "access_code.backup_access_code_pulled", + "access_code.unmanaged.converted_to_managed", + "access_code.unmanaged.failed_to_convert_to_managed", + "access_code.unmanaged.created", + "access_code.unmanaged.removed", + "access_grant.created", + "access_grant.deleted", + "access_grant.access_granted_to_all_doors", + "access_grant.access_granted_to_door", + "access_grant.access_to_door_lost", + "access_grant.access_times_changed", + "access_grant.could_not_create_requested_access_methods", + "access_method.issued", + "access_method.revoked", + "access_method.card_encoding_required", + "access_method.deleted", + "access_method.reissued", + "access_method.created", + "access_method.delay_in_issuing", + "access_method.failed_to_issue", + "acs_system.connected", + "acs_system.added", + "acs_system.disconnected", + "acs_credential.deleted", + "acs_credential.issued", + "acs_credential.reissued", + "acs_credential.invalidated", + "acs_user.created", + "acs_user.deleted", + "acs_encoder.added", + "acs_encoder.removed", + "acs_access_group.deleted", + "acs_entrance.added", + "acs_entrance.removed", + "client_session.deleted", + "connected_account.connected", + "connected_account.created", + "connected_account.successful_login", + "connected_account.disconnected", + "connected_account.completed_first_sync", + "connected_account.deleted", + "connected_account.completed_first_sync_after_reconnection", + "connected_account.reauthorization_requested", + "action_attempt.lock_door.succeeded", + "action_attempt.lock_door.failed", + "action_attempt.unlock_door.succeeded", + "action_attempt.unlock_door.failed", + "action_attempt.simulate_keypad_code_entry.succeeded", + "action_attempt.simulate_keypad_code_entry.failed", + "action_attempt.simulate_manual_lock_via_keypad.succeeded", + "action_attempt.simulate_manual_lock_via_keypad.failed", + "connect_webview.login_succeeded", + "connect_webview.login_failed", + "device.connected", + "device.added", + "device.converted_to_unmanaged", + "device.unmanaged.converted_to_managed", + "device.unmanaged.connected", + "device.disconnected", + "device.unmanaged.disconnected", + "device.tampered", + "device.low_battery", + "device.battery_status_changed", + "device.removed", + "device.deleted", + "device.third_party_integration_detected", + "device.third_party_integration_no_longer_detected", + "device.salto.privacy_mode_activated", + "device.salto.privacy_mode_deactivated", + "device.connection_became_flaky", + "device.connection_stabilized", + "device.error.subscription_required", + "device.error.subscription_required.resolved", + "device.accessory_keypad_connected", + "device.accessory_keypad_disconnected", + "noise_sensor.noise_threshold_triggered", + "lock.locked", + "lock.unlocked", + "lock.access_denied", + "thermostat.climate_preset_activated", + "thermostat.manually_adjusted", + "thermostat.temperature_threshold_exceeded", + "thermostat.temperature_threshold_no_longer_exceeded", + "thermostat.temperature_reached_set_point", + "thermostat.temperature_changed", + "device.name_changed", + "camera.activated", + "device.doorbell_rang", + "enrollment_automation.deleted", + "phone.deactivated", + "space.device_membership_changed", + "space.created", + "space.deleted", + ] + ] + ] = None, limit: Optional[float] = None, since: Optional[str] = None, space_id: Optional[str] = None, @@ -652,4 +1556,4 @@ async def list( res = await self.client.get("/events/list", params=params) - return [SeamEvent.from_dict(item) for item in res["events"]] + return [seam_event_from_dict(item) for item in res["events"]] diff --git a/seam/routes/locks.py b/seam/routes/locks.py index 88ae0d43..d150e87e 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -2,7 +2,7 @@ import abc from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata -from ..resources import ActionAttempt, Device +from ..resources import ActionAttempt, Device, action_attempt_from_dict from .locks_simulate import ( AbstractLocksSimulate, LocksSimulate, @@ -71,9 +71,115 @@ def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "brivo", + "butterflymx", + "avigilon_alta", + "doorking", + "genie", + "igloo", + "linear", + "lockly", + "kwikset", + "nuki", + "salto", + "schlage", + "seam", + "wyze", + "yale", + "two_n", + "controlbyweb", + "ttlock", + "igloohome", + "four_suites", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "korelock", + "smartthings", + "ultraloq", + "omnitec", + "kisi", + "yacan", + ] + ] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -185,9 +291,115 @@ async def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "brivo", + "butterflymx", + "avigilon_alta", + "doorking", + "genie", + "igloo", + "linear", + "lockly", + "kwikset", + "nuki", + "salto", + "schlage", + "seam", + "wyze", + "yale", + "two_n", + "controlbyweb", + "ttlock", + "igloohome", + "four_suites", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "korelock", + "smartthings", + "ultraloq", + "omnitec", + "kisi", + "yacan", + ] + ] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -303,7 +515,7 @@ def configure_auto_lock( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -348,9 +560,115 @@ def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "brivo", + "butterflymx", + "avigilon_alta", + "doorking", + "genie", + "igloo", + "linear", + "lockly", + "kwikset", + "nuki", + "salto", + "schlage", + "seam", + "wyze", + "yale", + "two_n", + "controlbyweb", + "ttlock", + "igloohome", + "four_suites", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "korelock", + "smartthings", + "ultraloq", + "omnitec", + "kisi", + "yacan", + ] + ] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -422,7 +740,7 @@ def lock_door( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -464,7 +782,7 @@ def unlock_door( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -529,7 +847,7 @@ async def configure_auto_lock( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -574,9 +892,115 @@ async def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "akuvox_lock", + "august_lock", + "brivo_access_point", + "butterflymx_panel", + "avigilon_alta_entry", + "doorking_lock", + "genie_door", + "igloo_lock", + "linear_lock", + "lockly_lock", + "kwikset_lock", + "nuki_lock", + "salto_lock", + "schlage_lock", + "smartthings_lock", + "wyze_lock", + "yale_lock", + "two_n_intercom", + "controlbyweb_device", + "ttlock_lock", + "igloohome_lock", + "four_suites_door", + "dormakaba_oracode_door", + "tedee_lock", + "akiles_lock", + "ultraloq_lock", + "yacan_lock", + "keyincode_lock", + "omnitec_lock", + "kisi_lock", + "aqara_lock", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "akuvox", + "august", + "brivo", + "butterflymx", + "avigilon_alta", + "doorking", + "genie", + "igloo", + "linear", + "lockly", + "kwikset", + "nuki", + "salto", + "schlage", + "seam", + "wyze", + "yale", + "two_n", + "controlbyweb", + "ttlock", + "igloohome", + "four_suites", + "dormakaba_oracode", + "tedee", + "keyincode", + "akiles", + "aqara", + "korelock", + "smartthings", + "ultraloq", + "omnitec", + "kisi", + "yacan", + ] + ] = None, ) -> List[Device]: """Returns a list of all `locks `_. @@ -648,7 +1072,7 @@ async def lock_door( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -690,6 +1114,6 @@ async def unlock_door( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/routes/locks_simulate.py b/seam/routes/locks_simulate.py index 3c6131ab..4b75249a 100644 --- a/seam/routes/locks_simulate.py +++ b/seam/routes/locks_simulate.py @@ -2,7 +2,7 @@ import abc from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata -from ..resources import ActionAttempt +from ..resources import ActionAttempt, action_attempt_from_dict from ..modules.action_attempts import ( resolve_action_attempt, resolve_action_attempt_async, @@ -143,7 +143,7 @@ def keypad_code_entry( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -189,7 +189,7 @@ def manual_lock_via_keypad( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -246,7 +246,7 @@ async def keypad_code_entry( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -292,6 +292,6 @@ async def manual_lock_via_keypad( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/routes/noise_sensors.py b/seam/routes/noise_sensors.py index ef6515b3..f6ddc74c 100644 --- a/seam/routes/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -36,9 +36,13 @@ def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal["noiseaware_activity_zone", "minut_sensor"] + ] = None, + device_types: Optional[ + List[Literal["noiseaware_activity_zone", "minut_sensor"]] + ] = None, + manufacturer: Optional[Literal["minut", "noiseaware"]] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. @@ -77,9 +81,13 @@ async def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal["noiseaware_activity_zone", "minut_sensor"] + ] = None, + device_types: Optional[ + List[Literal["noiseaware_activity_zone", "minut_sensor"]] + ] = None, + manufacturer: Optional[Literal["minut", "noiseaware"]] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. @@ -125,9 +133,13 @@ def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal["noiseaware_activity_zone", "minut_sensor"] + ] = None, + device_types: Optional[ + List[Literal["noiseaware_activity_zone", "minut_sensor"]] + ] = None, + manufacturer: Optional[Literal["minut", "noiseaware"]] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. @@ -190,9 +202,13 @@ async def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal["noiseaware_activity_zone", "minut_sensor"] + ] = None, + device_types: Optional[ + List[Literal["noiseaware_activity_zone", "minut_sensor"]] + ] = None, + manufacturer: Optional[Literal["minut", "noiseaware"]] = None, ) -> List[Device]: """Returns a list of all `noise sensors `_. diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py index 05a8300d..cda8006d 100644 --- a/seam/routes/spaces.py +++ b/seam/routes/spaces.py @@ -104,8 +104,30 @@ def get( def get_related( self, *, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, ) -> Batch: @@ -315,8 +337,30 @@ async def get( async def get_related( self, *, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, ) -> Batch: @@ -631,8 +675,30 @@ def get( def get_related( self, *, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, ) -> Batch: @@ -1057,8 +1123,30 @@ async def get( async def get_related( self, *, - exclude: Optional[List[str]] = None, - include: Optional[List[str]] = None, + exclude: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, + include: Optional[ + List[ + Literal[ + "spaces", + "devices", + "acs_entrances", + "connected_accounts", + "acs_systems", + "access_methods", + ] + ] + ] = None, space_ids: Optional[List[str]] = None, space_keys: Optional[List[str]] = None, ) -> Batch: diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index a0574006..e2cbde9d 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -3,7 +3,7 @@ from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata from ..null import Null -from ..resources import ActionAttempt, Device +from ..resources import ActionAttempt, Device, action_attempt_from_dict from .thermostats_daily_programs import ( AbstractThermostatsDailyPrograms, ThermostatsDailyPrograms, @@ -96,14 +96,18 @@ def create_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -208,9 +212,33 @@ def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "ecobee", "honeywell_resideo", "nest", "sensi", "smartthings", "tado" + ] + ] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -265,8 +293,8 @@ def set_fan_mode( self, *, device_id: str, - fan_mode: Optional[str] = None, - fan_mode_setting: Optional[str] = None, + fan_mode: Optional[Literal["auto", "on", "circulate"]] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `fan mode setting `_ for a specified `thermostat `_. @@ -289,7 +317,7 @@ def set_hvac_mode( self, *, device_id: str, - hvac_mode_setting: str, + hvac_mode_setting: Literal["off", "cool", "heat", "heat_cool", "eco"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, @@ -348,14 +376,18 @@ def update_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -496,14 +528,18 @@ async def create_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -610,9 +646,33 @@ async def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "ecobee", "honeywell_resideo", "nest", "sensi", "smartthings", "tado" + ] + ] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -667,8 +727,8 @@ async def set_fan_mode( self, *, device_id: str, - fan_mode: Optional[str] = None, - fan_mode_setting: Optional[str] = None, + fan_mode: Optional[Literal["auto", "on", "circulate"]] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `fan mode setting `_ for a specified `thermostat `_. @@ -691,7 +751,7 @@ async def set_hvac_mode( self, *, device_id: str, - hvac_mode_setting: str, + hvac_mode_setting: Literal["off", "cool", "heat", "heat_cool", "eco"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, @@ -750,14 +810,18 @@ async def update_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -899,7 +963,7 @@ def activate_climate_preset( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -949,7 +1013,7 @@ def cool( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -963,14 +1027,18 @@ def create_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -1112,7 +1180,7 @@ def heat( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1176,7 +1244,7 @@ def heat_cool( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1189,9 +1257,33 @@ def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "ecobee", "honeywell_resideo", "nest", "sensi", "smartthings", "tado" + ] + ] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -1263,7 +1355,7 @@ def off( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1307,8 +1399,8 @@ def set_fan_mode( self, *, device_id: str, - fan_mode: Optional[str] = None, - fan_mode_setting: Optional[str] = None, + fan_mode: Optional[Literal["auto", "on", "circulate"]] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `fan mode setting `_ for a specified `thermostat `_. @@ -1348,7 +1440,7 @@ def set_fan_mode( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1361,7 +1453,7 @@ def set_hvac_mode( self, *, device_id: str, - hvac_mode_setting: str, + hvac_mode_setting: Literal["off", "cool", "heat", "heat_cool", "eco"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, @@ -1417,7 +1509,7 @@ def set_hvac_mode( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1480,14 +1572,18 @@ def update_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -1629,7 +1725,7 @@ def update_weekly_program( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1703,7 +1799,7 @@ async def activate_climate_preset( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1753,7 +1849,7 @@ async def cool( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1767,14 +1863,18 @@ async def create_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -1918,7 +2018,7 @@ async def heat( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1982,7 +2082,7 @@ async def heat_cool( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -1995,9 +2095,33 @@ async def list( connect_webview_id: Optional[str] = None, connected_account_id: Optional[str] = None, customer_key: Optional[str] = None, - device_type: Optional[str] = None, - device_types: Optional[List[str]] = None, - manufacturer: Optional[str] = None, + device_type: Optional[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] = None, + device_types: Optional[ + List[ + Literal[ + "ecobee_thermostat", + "nest_thermostat", + "honeywell_resideo_thermostat", + "tado_thermostat", + "sensi_thermostat", + "smartthings_thermostat", + ] + ] + ] = None, + manufacturer: Optional[ + Literal[ + "ecobee", "honeywell_resideo", "nest", "sensi", "smartthings", "tado" + ] + ] = None, ) -> List[Device]: """Returns a list of all `thermostats `_. @@ -2069,7 +2193,7 @@ async def off( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -2115,8 +2239,8 @@ async def set_fan_mode( self, *, device_id: str, - fan_mode: Optional[str] = None, - fan_mode_setting: Optional[str] = None, + fan_mode: Optional[Literal["auto", "on", "circulate"]] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, wait_for_action_attempt: Optional[Union[bool, Dict[str, float]]] = None, ) -> ActionAttempt: """Sets the `fan mode setting `_ for a specified `thermostat `_. @@ -2156,7 +2280,7 @@ async def set_fan_mode( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -2169,7 +2293,7 @@ async def set_hvac_mode( self, *, device_id: str, - hvac_mode_setting: str, + hvac_mode_setting: Literal["off", "cool", "heat", "heat_cool", "eco"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, @@ -2225,7 +2349,7 @@ async def set_hvac_mode( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -2290,14 +2414,18 @@ async def update_climate_preset( *, climate_preset_key: str, device_id: str, - climate_preset_mode: Optional[str] = None, + climate_preset_mode: Optional[ + Literal["home", "away", "wake", "sleep", "occupied", "unoccupied"] + ] = None, cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, ecobee_metadata: Optional[Dict[str, Any]] = None, - fan_mode_setting: Optional[str] = None, + fan_mode_setting: Optional[Literal["auto", "on", "circulate"]] = None, heating_set_point_celsius: Optional[float] = None, heating_set_point_fahrenheit: Optional[float] = None, - hvac_mode_setting: Optional[str] = None, + hvac_mode_setting: Optional[ + Literal["off", "heat", "cool", "heat_cool", "eco"] + ] = None, manual_override_allowed: Optional[bool] = None, name: Optional[Union[str, Null]] = None, ) -> None: @@ -2441,6 +2569,6 @@ async def update_weekly_program( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/routes/thermostats_daily_programs.py b/seam/routes/thermostats_daily_programs.py index c3e23b56..da3dc49d 100644 --- a/seam/routes/thermostats_daily_programs.py +++ b/seam/routes/thermostats_daily_programs.py @@ -2,7 +2,7 @@ import abc from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata -from ..resources import ThermostatDailyProgram, ActionAttempt +from ..resources import ThermostatDailyProgram, ActionAttempt, action_attempt_from_dict from ..modules.action_attempts import ( resolve_action_attempt, resolve_action_attempt_async, @@ -232,7 +232,7 @@ def update( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -358,6 +358,6 @@ async def update( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/routes/thermostats_simulate.py b/seam/routes/thermostats_simulate.py index 89c6f56c..b7487f8d 100644 --- a/seam/routes/thermostats_simulate.py +++ b/seam/routes/thermostats_simulate.py @@ -11,7 +11,7 @@ def hvac_mode_adjusted( self, *, device_id: str, - hvac_mode: str, + hvac_mode: Literal["off", "cool", "heat", "heat_cool"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, @@ -61,7 +61,7 @@ async def hvac_mode_adjusted( self, *, device_id: str, - hvac_mode: str, + hvac_mode: Literal["off", "cool", "heat", "heat_cool"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, @@ -118,7 +118,7 @@ def hvac_mode_adjusted( self, *, device_id: str, - hvac_mode: str, + hvac_mode: Literal["off", "cool", "heat", "heat_cool"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, @@ -217,7 +217,7 @@ async def hvac_mode_adjusted( self, *, device_id: str, - hvac_mode: str, + hvac_mode: Literal["off", "cool", "heat", "heat_cool"], cooling_set_point_celsius: Optional[float] = None, cooling_set_point_fahrenheit: Optional[float] = None, heating_set_point_celsius: Optional[float] = None, diff --git a/seam/routes/workspaces.py b/seam/routes/workspaces.py index 2573ea95..27d6f872 100644 --- a/seam/routes/workspaces.py +++ b/seam/routes/workspaces.py @@ -3,7 +3,7 @@ from ..client import SeamHttpClient, AsyncSeamHttpClient from ..route import route_metadata from ..null import Null -from ..resources import Workspace, ActionAttempt +from ..resources import Workspace, ActionAttempt, action_attempt_from_dict from ..modules.action_attempts import ( resolve_action_attempt, resolve_action_attempt_async, @@ -22,7 +22,7 @@ def create( connect_webview_customization: Optional[Dict[str, Any]] = None, is_sandbox: Optional[bool] = None, organization_id: Optional[str] = None, - webview_logo_shape: Optional[str] = None, + webview_logo_shape: Optional[Literal["circle", "square"]] = None, webview_primary_button_color: Optional[str] = None, webview_primary_button_text_color: Optional[str] = None, webview_success_message: Optional[str] = None, @@ -119,7 +119,7 @@ async def create( connect_webview_customization: Optional[Dict[str, Any]] = None, is_sandbox: Optional[bool] = None, organization_id: Optional[str] = None, - webview_logo_shape: Optional[str] = None, + webview_logo_shape: Optional[Literal["circle", "square"]] = None, webview_primary_button_color: Optional[str] = None, webview_primary_button_text_color: Optional[str] = None, webview_success_message: Optional[str] = None, @@ -221,7 +221,7 @@ def create( connect_webview_customization: Optional[Dict[str, Any]] = None, is_sandbox: Optional[bool] = None, organization_id: Optional[str] = None, - webview_logo_shape: Optional[str] = None, + webview_logo_shape: Optional[Literal["circle", "square"]] = None, webview_primary_button_color: Optional[str] = None, webview_primary_button_text_color: Optional[str] = None, webview_success_message: Optional[str] = None, @@ -338,7 +338,7 @@ def reset_sandbox( return resolve_action_attempt( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) @@ -410,7 +410,7 @@ async def create( connect_webview_customization: Optional[Dict[str, Any]] = None, is_sandbox: Optional[bool] = None, organization_id: Optional[str] = None, - webview_logo_shape: Optional[str] = None, + webview_logo_shape: Optional[Literal["circle", "square"]] = None, webview_primary_button_color: Optional[str] = None, webview_primary_button_text_color: Optional[str] = None, webview_success_message: Optional[str] = None, @@ -527,7 +527,7 @@ async def reset_sandbox( return await resolve_action_attempt_async( client=self.client, - action_attempt=ActionAttempt.from_dict(res["action_attempt"]), + action_attempt=action_attempt_from_dict(res["action_attempt"]), wait_for_action_attempt=wait_for_action_attempt, ) diff --git a/seam/seam_webhook.py b/seam/seam_webhook.py index 2a56c631..9f44883e 100644 --- a/seam/seam_webhook.py +++ b/seam/seam_webhook.py @@ -1,6 +1,6 @@ from typing import Dict from svix.webhooks import Webhook -from .resources import SeamEvent +from .resources import SeamEvent, seam_event_from_dict class SeamWebhook: @@ -30,4 +30,4 @@ def verify(self, payload: str, headers: Dict[str, str]) -> SeamEvent: normalized_headers = {k.lower(): v for k, v in headers.items()} res = self._webhook.verify(payload, normalized_headers) - return SeamEvent.from_dict(res) + return seam_event_from_dict(res) diff --git a/test/nested_resource_test.py b/test/nested_resource_test.py index 74fb7823..f0654b20 100644 --- a/test/nested_resource_test.py +++ b/test/nested_resource_test.py @@ -1,20 +1,29 @@ """Regression tests for generated nested resource types.""" import dataclasses +from typing import Any, cast import pytest import seam.resources.device as device_module from seam.resources.acs_user import AcsUser -from seam.resources.action_attempt import ActionAttempt +from seam.resources.action_attempt import ( + LockDoorActionAttempt, + ScanCredentialActionAttempt, + action_attempt_from_dict, +) from seam.resources.device import Device +from seam.resources.seam_event import ( + AccessCodeCreatedEvent, + seam_event_from_dict, +) def test_nested_objects_are_typed_and_drop_unknown_fields(): device = Device.from_dict( { "properties": {"locked": True, "future_api_field": "ignored"}, - "errors": [{"error_code": "offline", "message": "Offline"}], + "errors": [{"error_code": "device_offline", "message": "Offline"}], "custom_metadata": {"arbitrary": {"future": True}}, } ) @@ -22,8 +31,8 @@ def test_nested_objects_are_typed_and_drop_unknown_fields(): assert isinstance(device.properties, Device.Properties) assert device.properties.locked is True assert not hasattr(device.properties, "future_api_field") - assert isinstance(device.errors[0], Device.Errors) - assert device.errors[0].error_code == "offline" + assert isinstance(device.errors[0], Device.DeviceOfflineError) + assert device.errors[0].error_code == "device_offline" assert device.custom_metadata["arbitrary"]["future"] is True @@ -48,55 +57,68 @@ def test_missing_nested_values_use_stable_defaults(): assert len(device.errors) == 0 +def test_event_union_dispatches_and_keeps_unknown_events_readable(): + event = seam_event_from_dict({"event_type": "access_code.created"}) + unknown = cast( + Any, + seam_event_from_dict( + {"event_type": "future.event", "future_api_field": "kept"} + ), + ) + + assert isinstance(event, AccessCodeCreatedEvent) + assert unknown.event_type == "future.event" + assert unknown.future_api_field == "kept" + + def test_action_attempt_union_hydrates_nested_result_and_error(): - attempt = ActionAttempt.from_dict( + attempt = action_attempt_from_dict( { + "action_type": "LOCK_DOOR", "result": {"was_confirmed_by_device": True}, "error": {"message": "failed", "type": "device_error"}, } ) - assert isinstance(attempt.result, ActionAttempt.Result) + assert isinstance(attempt, LockDoorActionAttempt) + assert isinstance(attempt.result, LockDoorActionAttempt.Result) assert attempt.result.was_confirmed_by_device is True - assert isinstance(attempt.error, ActionAttempt.Error) + assert isinstance(attempt.error, LockDoorActionAttempt.Error) assert attempt.error.message == "failed" - -def test_merged_variants_keep_every_variant_field(): - result_fields = {f.name for f in dataclasses.fields(ActionAttempt.Result)} - - assert "was_confirmed_by_device" in result_fields - assert "acs_credential_on_encoder" in result_fields - assert "instant_key_url" in result_fields - - encoded = ActionAttempt.from_dict( - { - "action_type": "ENCODE_ACS_CREDENTIAL", - "result": { - "acs_credential_on_encoder": {"card_number": "123"}, - "acs_credential_on_seam": {"acs_credential_id": "cred_1"}, - }, - } - ) - assert encoded.result.acs_credential_on_encoder.card_number == "123" - assert encoded.result.acs_credential_on_seam.acs_credential_id == "cred_1" - - instant_key = ActionAttempt.from_dict( - { - "action_type": "CREATE_INSTANT_KEY", - "result": {"instant_key_url": "https://x"}, - } + pending = action_attempt_from_dict( + {"action_type": "LOCK_DOOR", "status": "pending"} ) - assert instant_key.result.instant_key_url == "https://x" - - -def test_merged_variants_recurse_into_nested_objects(): - from_fields = {f.name for f in dataclasses.fields(AcsUser.PendingMutations.From)} - - assert "full_name" in from_fields - assert "starts_at" in from_fields - assert "is_suspended" in from_fields - assert "acs_access_group_id" in from_fields + 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)} + scan_fields = { + f.name for f in dataclasses.fields(ScanCredentialActionAttempt.Result) + } + + assert "was_confirmed_by_device" in lock_fields + assert "acs_credential_on_encoder" not in lock_fields + assert "acs_credential_on_encoder" in scan_fields + assert "was_confirmed_by_device" not in scan_fields + + +def test_discriminated_list_variants_keep_distinct_nested_objects(): + information_fields = { + f.name + for f in dataclasses.fields(AcsUser.UpdatingUserInformationPendingMutation.From) + } + schedule_fields = { + f.name + for f in dataclasses.fields(AcsUser.UpdatingAccessSchedulePendingMutation.From) + } + + assert "full_name" in information_fields + assert "starts_at" not in information_fields + assert "starts_at" in schedule_fields + assert "full_name" not in schedule_fields def test_same_named_nested_objects_keep_distinct_shapes(): diff --git a/test/resource_types_test.py b/test/resource_types_test.py index a7d29d86..e2b29c9e 100644 --- a/test/resource_types_test.py +++ b/test/resource_types_test.py @@ -4,8 +4,12 @@ from seam.resources import ( AccessCode, + AccessCodeCreatedEvent, ActionAttempt, Device, + LockDoorActionAttempt, + NoiseSensorNoiseThresholdTriggeredEvent, + ScanCredentialActionAttempt, SeamEvent, UnmanagedAccessCode, ) @@ -23,29 +27,58 @@ def _assert_access_code_narrowing( def _assert_boolean_shapes( code: AccessCode, unmanaged_code: UnmanagedAccessCode, - credential: ActionAttempt.Result.AcsCredentialOnSeam, + credential: ScanCredentialActionAttempt.Result.AcsCredentialOnSeam, ) -> None: assert_type(code.is_backup_access_code_available, bool) - assert_type(code.errors[0].is_access_code_error, Literal[True] | None) - assert_type(unmanaged_code.errors[0].is_connected_account_error, bool | None) + error = code.errors[0] + if error.error_code == "failed_to_set_on_device": + assert_type(error, AccessCode.FailedToSetOnDeviceError) + assert_type(error.is_access_code_error, Literal[True]) + + unmanaged_error = unmanaged_code.errors[0] + if unmanaged_error.error_code == "account_disconnected": + assert_type( + unmanaged_error, + UnmanagedAccessCode.AccountDisconnectedError, + ) + assert_type(unmanaged_error.is_connected_account_error, Literal[True]) + assert_type(credential.is_managed, Literal[True, False]) -def _assert_record_value_types(device: Device, event: SeamEvent) -> None: +def _assert_event_narrowing(event: SeamEvent) -> None: + if event.event_type == "access_code.created": + assert_type(event, AccessCodeCreatedEvent) + assert_type( + event.connected_account_custom_metadata, + Dict[str, Union[str, bool]] | None, + ) + elif event.event_type == "noise_sensor.noise_threshold_triggered": + assert_type(event, NoiseSensorNoiseThresholdTriggeredEvent) + assert_type(event.minut_metadata, Dict[str, Any] | None) + + +def _assert_action_attempt_narrowing(attempt: ActionAttempt) -> None: + if attempt.action_type == "LOCK_DOOR": + assert_type(attempt, LockDoorActionAttempt) + assert_type( + attempt.result, + LockDoorActionAttempt.Result | None, + ) + + +def _assert_record_value_types(device: Device) -> None: assert_type(device.custom_metadata, Dict[str, Union[str, bool]]) - assert_type( - event.connected_account_custom_metadata, - Dict[str, Union[str, bool]] | None, - ) - assert_type(event.minut_metadata, Dict[str, Any] | None) def _assert_opposite_literal_is_rejected(code: UnmanagedAccessCode) -> None: code.is_managed = True # type: ignore[assignment] -def test_access_code_resources_narrow_on_is_managed(): +def test_resource_types_narrow_on_discriminants(): assert callable(_assert_access_code_narrowing) assert callable(_assert_boolean_shapes) + assert callable(_assert_event_narrowing) + assert callable(_assert_action_attempt_narrowing) assert callable(_assert_record_value_types) assert callable(_assert_opposite_literal_is_rejected)